c32/
lib.rs

1// © 2025 Max Karou. All Rights Reserved.
2// Licensed under Apache Version 2.0, or MIT License, at your discretion.
3//
4// Apache License: http://www.apache.org/licenses/LICENSE-2.0
5// MIT License: http://opensource.org/licenses/MIT
6//
7// Usage of this file is permitted solely under a sanctioned license.
8
9#![no_std]
10#![allow(clippy::doc_markdown)]
11#![allow(clippy::wildcard_imports)]
12#![allow(clippy::missing_errors_doc)]
13#![allow(clippy::items_after_statements)]
14#![cfg_attr(docsrs, feature(doc_auto_cfg))]
15#![cfg_attr(docsrs, feature(doc_alias))]
16
17//! [![Crates.io](https://img.shields.io/crates/v/c32.svg)][Crates.io]
18//! [![Documentation](https://docs.rs/c32/badge.svg)][Docs.rs]
19//! [![Build Status](https://img.shields.io/github/actions/workflow/status/52/c32/rust.yml?branch=master)][Workflow]
20//! [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)][License-Apache]
21//! [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)][License-MIT]
22//!
23//! Rust implementation of [Crockford's Base32][Crockford] encoding scheme.
24//!
25//! ## Implementation
26//!
27//! * **Lightweight** — The core functionality has zero external dependencies.
28//! * **Portable** — Fully compatible with `#![no_std]` environments.
29//! * **Safe** — Implemented entirely in safe Rust with no `unsafe` blocks.
30//!
31//! ```rust
32//! # #[cfg(feature = "alloc")] {
33//! let bytes = b"usque ad finem";
34//! let encoded = c32::encode(&bytes);
35//! assert_eq!(encoded, "1TQ6WBNCMG62S10CSMPWSBD");
36//! # }
37//! # Ok::<(), c32::Error>(())
38//! ```
39//!
40//! ```rust
41//! # #[cfg(feature = "alloc")] {
42//! let bytes = b"usque ad finem";
43//! let decoded = c32::decode("1TQ6WBNCMG62S10CSMPWSBD")?;
44//! assert_eq!(decoded, bytes);
45//! # }
46//! # Ok::<(), c32::Error>(())
47//! ```
48//!
49//! ## In `#![no_std]` Environments
50//!
51//! For environments without allocation support, the library provides
52//! buffer-based APIs:
53//!
54//! ```rust
55//! // encoding with a pre-allocated buffer
56//! let bytes = b"usque ad finem";
57//! let mut buffer = [0; 32];
58//!
59//! let written = c32::encode_into(bytes, &mut buffer)?;
60//! let encoded = &buffer[..written];
61//! assert_eq!(encoded, b"1TQ6WBNCMG62S10CSMPWSBD");
62//! # Ok::<(), c32::Error>(())
63//! ```
64//!
65//! ```rust
66//! // decoding with a pre-allocated buffer
67//! let encoded = b"1TQ6WBNCMG62S10CSMPWSBD";
68//! let mut buffer = [0; 32];
69//!
70//! let written = c32::decode_into(encoded, &mut buffer)?;
71//! let decoded = &buffer[..written];
72//! assert_eq!(decoded, b"usque ad finem");
73//! # Ok::<(), c32::Error>(())
74//! ```
75//!
76//! ## Checksums
77//!
78//! The `check` feature enables methods for encoding data with SHA256-based
79//! checksum verification.
80//!
81//! The encoded data follows this layout:
82//! ```text
83//! [version (1B)] + [payload (nB)] + [checksum (4B)]
84//! ```
85//!
86//! And is computed by...
87//! ```text
88//! 1. Concatenating the version byte with the payload bytes.
89//! 2. Taking the SHA256 hash of the concatenated bytes.
90//! 3. Taking the SHA256 hash of the result.
91//! 4. Using the first 4 bytes as the checksum.
92//! ```
93//!
94//! ### Examples
95//! ```rust
96//! # #[cfg(all(feature = "check", feature = "alloc"))] {
97//! let bytes = b"usque ad finem";
98//! let encoded = c32::encode_check(bytes, 22)?;
99//! assert_eq!(encoded, "P7AWVHENJJ0RB441K6JVK5DNJ7J3V5");
100//! # }
101//! # Ok::<(), c32::Error>(())
102//! ```
103//! ```rust
104//! # #[cfg(all(feature = "check", feature = "alloc"))] {
105//! let encoded = "P7AWVHENJJ0RB441K6JVK5DNJ7J3V5";
106//! let (decoded, version) = c32::decode_check(encoded)?;
107//! assert_eq!(decoded, b"usque ad finem");
108//! assert_eq!(version, 22);
109//! # }
110//! # Ok::<(), c32::Error>(())
111//! ```
112//!
113//! # Features
114//!
115//!  Feature | Description
116//! ---------|-------------------------------------------------------------
117//!  `alloc` | Allocation-based API via [`encode`] and [`decode`]
118//!  `check` | Support for checksum validation
119//!
120//! For more details, please refer to the full [API Reference][Docs.rs].
121//!
122//! [Crates.io]: https://crates.io/crates/c32
123//! [Docs.rs]: https://docs.rs/c32
124//! [Workflow]: https://github.com/52/c32/actions
125//! [License-Apache]: https://opensource.org/licenses/Apache-2.0
126//! [License-MIT]: https://opensource.org/licenses/MIT
127//! [Crockford]: https://www.crockford.com/base32.html
128
129#[cfg(feature = "alloc")]
130extern crate alloc;
131
132use core::error;
133use core::fmt;
134use core::marker;
135use core::slice;
136use core::str;
137
138/// Re-exports for feature compatibility.
139///
140/// This module exports common allocation types.
141#[cfg(feature = "alloc")]
142pub(crate) mod __private {
143    pub use alloc::string::String;
144    pub use alloc::vec;
145    pub use alloc::vec::Vec;
146}
147
148#[cfg(feature = "alloc")]
149pub(crate) use __private::*;
150
151/// This module provides methods for computing [`SHA-256`] checksums.
152///
153/// [`SHA-256`]: https://helix.stormhub.org/papers/SHA-256.pdf
154#[cfg(feature = "check")]
155pub mod checksum {
156    use sha2::Sha256;
157
158    use super::*;
159
160    /// Length of the [`Checksum`] in bytes.
161    pub const BYTE_LENGTH: usize = 4;
162
163    /// A type alias for a [`Sha256`] checksum.
164    pub type Checksum = [u8; BYTE_LENGTH];
165
166    /// Computes a 4-byte [`Checksum`]from a byte array and version.
167    ///
168    /// The checksum is computed by:
169    ///
170    /// 1. Concatenating the version byte with the payload bytes.
171    /// 2. Taking the SHA256 hash of the concatenated bytes.
172    /// 3. Taking the SHA256 hash of the result.
173    /// 4. Using the first 4 bytes as the checksum.
174    ///
175    /// # Examples
176    ///
177    /// ```rust
178    /// use c32::checksum;
179    ///
180    /// let bytes = [42, 42, 42];
181    /// let sum = checksum::compute(&bytes, 0);
182    /// assert_eq!(sum.len(), 4);
183    /// ```
184    #[inline]
185    #[must_use]
186    pub const fn compute(bytes: &[u8], version: u8) -> Checksum {
187        let buffer = Sha256::new().update(&[version]).update(bytes).finalize();
188        let hash = Sha256::new().update(&buffer).finalize();
189        from_slice(&hash)
190    }
191
192    /// Creates a [`Checksum`] from a byte slice.
193    ///
194    /// # Examples
195    ///
196    /// ```rust
197    /// use c32::checksum;
198    ///
199    /// let hash = [1, 2, 3, 4, 5, 6, 7, 8];
200    /// let sum = checksum::from_slice(&hash);
201    /// assert_eq!(sum, [1, 2, 3, 4]);
202    /// ```
203    #[inline]
204    #[must_use]
205    pub const fn from_slice(bytes: &[u8]) -> Checksum {
206        let mut sum = [0u8; BYTE_LENGTH];
207        __internal::memcpy(&mut sum, 0, bytes, 0, BYTE_LENGTH);
208        sum
209    }
210}
211
212/// The Crockford Base32 alphabet used for encoding and decoding.
213pub(crate) const ALPHABET: &[u8; 32] = b"0123456789ABCDEFGHJKMNPQRSTVWXYZ";
214
215/// A mapping from ASCII characters to their Crockford Base32 values.
216pub(crate) const BYTE_MAP: [i8; 128] = [
217    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
218    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
219    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1,
220    -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 1, 18, 19, 1, 20,
221    21, 0, 22, 23, 24, 25, 26, -1, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, -1,
222    10, 11, 12, 13, 14, 15, 16, 17, 1, 18, 19, 1, 20, 21, 0, 22, 23, 24, 25,
223    26, -1, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1,
224];
225
226/// Error variants for fallible Crockford Base32 operations.
227#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
228pub enum Error {
229    /// The buffer size is insufficient for the operation.
230    ///
231    /// # Fields
232    ///
233    /// * `min` - The minimum required buffer size.
234    /// * `len` - The actual size of the provided buffer.
235    BufferTooSmall { min: usize, len: usize },
236    /// The input data size doesn't match the expected size.
237    ///
238    /// # Fields
239    ///
240    /// * `expected` - The expected data size in bytes.
241    /// * `got` - The actual size of the provided data.
242    InvalidDataSize { expected: usize, got: usize },
243    /// An invalid character was encountered during decoding.
244    ///
245    /// # Fields
246    ///
247    /// * `char` - The invalid character found in the input.
248    /// * `index` - The byte index of the character.
249    InvalidCharacter { char: char, index: usize },
250    /// The expected prefix character is missing.
251    ///
252    /// # Fields
253    ///
254    /// * `char` - The expected prefix character.
255    /// * `got` - The actual first character found.
256    MissingPrefix { char: char, got: Option<char> },
257    #[cfg(feature = "check")]
258    /// The provided version byte is invalid.
259    ///
260    /// # Fields
261    ///
262    /// * `expected` - The expected version constraints.
263    /// * `version` - The invalid version byte.
264    InvalidVersion { expected: &'static str, version: u8 },
265    /// The input has fewer bytes than are required.
266    ///
267    /// # Fields
268    ///
269    /// * `min` - The minimum required amount of bytes.
270    /// * `len` - The actual number of bytes provided.
271    #[cfg(feature = "check")]
272    InsufficientData { min: usize, len: usize },
273    /// The computed checksum does not match the expected sum.
274    ///
275    /// # Fields
276    ///
277    /// * `expected` - The expected checksum.
278    /// * `got` - The actual checksum.
279    #[cfg(feature = "check")]
280    ChecksumMismatch {
281        expected: checksum::Checksum,
282        got: checksum::Checksum,
283    },
284}
285
286impl fmt::Display for Error {
287    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288        match self {
289            Self::BufferTooSmall { min, len } => {
290                write!(f, "Buffer size '{len}' is less than required '{min}'")
291            }
292            Self::InvalidDataSize { expected, got } => {
293                write!(f, "Invalid data size '{got}', expected: '{expected}'")
294            }
295            Self::InvalidCharacter { char, index } => {
296                write!(f, "Invalid character '{char}' at position {index}")
297            }
298            Self::MissingPrefix { char, got } => {
299                write!(f, "Expected prefix '{char}', found '{got:?}'")
300            }
301            #[cfg(feature = "check")]
302            Self::InvalidVersion { expected, version } => {
303                write!(f, "Invalid version byte '{version}': {expected}")
304            }
305            #[cfg(feature = "check")]
306            Self::InsufficientData { min, len } => {
307                write!(f, "Input size '{len}' is less than required '{min}'")
308            }
309            #[cfg(feature = "check")]
310            Self::ChecksumMismatch { expected, got } => {
311                write!(f, "Expected checksum '{expected:?}', got '{got:?}'")
312            }
313        }
314    }
315}
316
317impl error::Error for Error {}
318
319/// Result type for fallible Crockford Base32 operations.
320pub type Result<T> = core::result::Result<T, Error>;
321
322/// A marker trait for Crockford Base32 variations.
323///
324/// # Generics
325///
326/// * `PREFIX` - Whether to include a prefix character.
327pub trait Encoding<const PREFIX: bool> {}
328
329/// [`Encoding`] implementations.
330///
331/// This module exports various [`Encoding`] types.
332pub mod en {
333    use super::*;
334
335    /// Default Crockford Base32 encoding.
336    ///
337    /// # Generics
338    ///
339    /// * `PREFIX` - Whether to include a prefix character.
340    ///
341    /// # Examples
342    ///
343    /// ```rust
344    /// use c32::en::Default;
345    /// use c32::Buffer;
346    ///
347    /// // Default encoding w/o prefix
348    /// let en = Buffer::<5, false, Default>::encode(&[42, 42, 42]);
349    /// assert_eq!(en.as_str(), "2MAHA");
350    ///
351    /// // Default encoding with prefix
352    /// let en = Buffer::<6, true, Default>::encode(&[42, 42, 42], 'S');
353    /// assert_eq!(en.as_str(), "S2MAHA");
354    /// ```
355    pub struct Default;
356    impl<const PREFIX: bool> Encoding<PREFIX> for Default {}
357
358    #[cfg(feature = "check")]
359    mod __check {
360        use super::*;
361
362        /// Crockford Base32 encoding with checksum validation.
363        ///
364        /// This encoding format includes a 4-byte `Checksum`.
365        ///
366        /// # Generics
367        ///
368        /// * `PREFIX` - Whether to include a prefix character.
369        ///
370        /// # Examples
371        ///
372        /// ```rust
373        /// use c32::en::Check;
374        /// use c32::Buffer;
375        ///
376        /// // Check encoding w/o prefix
377        /// let en = Buffer::<13, false, Check>::encode(&[42, 42, 42], 0);
378        /// assert_eq!(en.as_str(), "0AHA59B9201Z");
379        ///
380        /// // Check encoding with prefix
381        /// let en = Buffer::<14, true, Check>::encode(&[42, 42, 42], 'S', 0);
382        /// assert_eq!(en.as_str(), "S0AHA59B9201Z");
383        /// ```
384        pub struct Check;
385        impl<const PREFIX: bool> Encoding<PREFIX> for Check {}
386    }
387
388    #[cfg(feature = "check")]
389    pub use __check::*;
390}
391
392/// A fixed-size buffer for encoding or decoding Crockford's Base32.
393///
394/// [`Buffer`] manages a fixed-size array of bytes and tracks the number of
395/// bytes written during encoding and decoding operations, and is primarily
396/// designed for use in a constant context.
397///
398/// # Generics
399///
400/// * `LEN` - The size of the byte array in bytes.
401/// * `PREFIX` - Whether to include a prefix character, defaults to `false`.
402/// * `E` - The [`Encoding`] format to use, defaults to [`en::Default`].
403///
404/// # Examples
405///
406/// ```rust
407/// use c32::Buffer;
408///
409/// const BYTES: [u8; 3] = [42, 42, 42];
410/// const EN: Buffer<5> = Buffer::<5>::encode(&BYTES);
411/// assert_eq!(EN.as_str(), "2MAHA");
412///
413/// const DE: Buffer<5> = Buffer::<5>::decode(EN.as_bytes());
414/// assert_eq!(DE.as_bytes(), [42, 42, 42]);
415/// ```
416pub struct Buffer<
417    const LEN: usize,
418    const PREFIX: bool = false,
419    E: Encoding<PREFIX> = en::Default,
420> {
421    /// The underlying byte array.
422    __raw: [u8; LEN],
423    /// The number of written bytes to the buffer.
424    __pos: usize,
425    /// The associated [`Encoding`] format `E`.
426    __marker: marker::PhantomData<E>,
427}
428
429impl<const LEN: usize, const PREFIX: bool, E: Encoding<PREFIX>>
430    Buffer<LEN, PREFIX, E>
431{
432    /// An empty buffer with no bytes written.
433    ///
434    /// # Examples
435    ///
436    /// ```rust
437    /// use c32::Buffer;
438    ///
439    /// let buffer = Buffer::<10>::EMPTY;
440    /// assert_eq!(buffer.pos(), 0);
441    /// assert_eq!(buffer.as_bytes(), &[]);
442    /// ```
443    pub const EMPTY: Self = Self {
444        __raw: [0u8; LEN],
445        __pos: 0,
446        __marker: marker::PhantomData,
447    };
448
449    /// Creates a new [`Buffer`].
450    ///
451    /// This is an internal method.
452    const fn new(__raw: [u8; LEN], __pos: usize) -> Self {
453        Self {
454            __raw,
455            __pos,
456            __marker: marker::PhantomData,
457        }
458    }
459
460    /// Returns the number of bytes written to the buffer.
461    ///
462    /// # Examples
463    ///
464    /// ```rust
465    /// use c32::Buffer;
466    ///
467    /// const INPUT: [u8; 3] = [42, 42, 42];
468    /// const EN: Buffer<5> = Buffer::<5>::encode(&INPUT);
469    /// assert_eq!(EN.pos(), 5);
470    /// ```
471    #[inline]
472    #[must_use]
473    pub const fn pos(&self) -> usize {
474        self.__pos
475    }
476
477    /// Returns a string slice of the written bytes.
478    ///
479    /// # Examples
480    ///
481    /// ```rust
482    /// use c32::Buffer;
483    ///
484    /// const INPUT: [u8; 3] = [42, 42, 42];
485    /// const EN: Buffer<5> = Buffer::<5>::encode(&INPUT);
486    /// assert_eq!(EN.as_str(), "2MAHA");
487    /// ```
488    #[inline]
489    #[must_use]
490    pub const fn as_str(&self) -> &str {
491        // SAFETY: We only push valid UTF-8 to `self.__raw`.
492        unsafe { str::from_utf8_unchecked(self.as_bytes()) }
493    }
494
495    /// Returns a slice of the written bytes.
496    ///
497    /// # Examples
498    ///
499    /// ```rust
500    /// use c32::Buffer;
501    ///
502    /// const INPUT: [u8; 3] = [42, 42, 42];
503    /// const EN: Buffer<5> = Buffer::<5>::encode(&INPUT);
504    /// assert_eq!(EN.as_bytes(), b"2MAHA");
505    /// ```
506    #[inline]
507    #[must_use]
508    pub const fn as_bytes(&self) -> &[u8] {
509        // SAFETY: `self.__pos` is always within bounds.
510        unsafe { slice::from_raw_parts(self.as_ptr(), self.__pos) }
511    }
512
513    /// Returns a raw pointer to the buffer's data.
514    ///
515    /// # Examples
516    ///
517    /// ```rust
518    /// # use c32::Buffer;
519    /// let buffer = Buffer::<10, false>::EMPTY;
520    /// let ptr = buffer.as_ptr();
521    /// ```
522    #[inline]
523    #[must_use]
524    pub const fn as_ptr(&self) -> *const u8 {
525        self.__raw.as_ptr()
526    }
527}
528
529impl<const N: usize> Buffer<N, false, en::Default> {
530    /// Encodes a byte array into a [`Buffer`].
531    ///
532    /// # Examples
533    ///
534    /// ```rust
535    /// use c32::Buffer;
536    ///
537    /// const INPUT: [u8; 3] = [42, 42, 42];
538    /// const EN: Buffer<5> = Buffer::<5>::encode(&INPUT);
539    /// assert_eq!(EN.as_str(), "2MAHA");
540    /// ```
541    #[inline]
542    #[must_use]
543    pub const fn encode<const M: usize>(src: &[u8; M]) -> Self {
544        const { assert!(N >= encoded_len(M), "Size 'N' is too small") }
545
546        // Allocate the output buffer.
547        let mut __raw = [0u8; N];
548
549        // Encode the input into the buffer.
550        let __pos = __internal::en(src, 0, M, &mut __raw, 0, None);
551
552        Self::new(__raw, __pos)
553    }
554
555    /// Encodes a byte array into a [`Buffer`].
556    ///
557    /// # Examples
558    ///
559    /// ```rust
560    /// use c32::Buffer;
561    /// use c32::Error;
562    ///
563    /// let input = [42, 42, 42];
564    /// let en = Buffer::<5>::try_encode(&input)?;
565    /// assert_eq!(en.as_str(), "2MAHA");
566    /// # Ok::<(), Error>(())
567    /// ```
568    #[inline]
569    pub const fn try_encode<const M: usize>(src: &[u8; M]) -> Result<Self> {
570        // Assert that the buffer has enough capacity.
571        let capacity = encoded_len(M);
572        if N < capacity {
573            return Err(Error::BufferTooSmall {
574                min: capacity,
575                len: N,
576            });
577        }
578
579        Ok(Self::encode(src))
580    }
581
582    /// Decodes a slice of encoded bytes into a [`Buffer`].
583    ///
584    /// # Examples
585    ///
586    /// ```rust
587    /// use c32::Buffer;
588    ///
589    /// const INPUT: [u8; 5] = *b"2MAHA";
590    /// const DE: Buffer<5> = Buffer::<5>::decode(&INPUT);
591    /// assert_eq!(DE.as_bytes(), [42, 42, 42]);
592    /// ```
593    #[inline]
594    #[must_use]
595    pub const fn decode(src: &[u8]) -> Self {
596        assert!(N >= decoded_len(src.len()), "Size 'N' is too small");
597
598        // Allocate the output buffer.
599        let mut __raw = [0u8; N];
600
601        // Decode the input to the buffer.
602        let __pos = match __internal::de(src, 0, src.len(), &mut __raw, 0) {
603            Ok(pos) => pos,
604            Err(Error::InvalidCharacter { char: _, index: _ }) => {
605                panic!("Input contains invalid characters")
606            }
607            _ => unreachable!(),
608        };
609
610        Self::new(__raw, __pos)
611    }
612
613    /// Decodes a slice of encoded bytes into a [`Buffer`].
614    ///
615    /// # Examples
616    ///
617    /// ```rust
618    /// # use c32::Error;
619    /// use c32::Buffer;
620    ///
621    /// let input = b"2MAHA";
622    /// let de = Buffer::<5>::try_decode(input)?;
623    /// assert_eq!(de.as_bytes(), [42, 42, 42]);
624    /// # Ok::<(), Error>(())
625    /// ```
626    #[inline]
627    pub const fn try_decode(src: &[u8]) -> Result<Self> {
628        // Assert that the buffer has enough capacity.
629        let capacity = decoded_len(src.len());
630        if N < capacity {
631            return Err(Error::BufferTooSmall {
632                min: capacity,
633                len: N,
634            });
635        }
636
637        Ok(Self::decode(src))
638    }
639}
640
641impl<const N: usize> Buffer<N, true, en::Default> {
642    /// Encodes a byte array with a prefix into a [`Buffer`].
643    ///
644    /// # Examples
645    ///
646    /// ```rust
647    /// use c32::Buffer;
648    ///
649    /// const INPUT: [u8; 3] = [42, 42, 42];
650    /// const EN: Buffer<6, true> = Buffer::<6, true>::encode(&INPUT, 'S');
651    /// assert_eq!(EN.as_str(), "S2MAHA");
652    /// ```
653    #[inline]
654    #[must_use]
655    pub const fn encode<const M: usize>(src: &[u8; M], prefix: char) -> Self {
656        const { assert!(N > encoded_len(M), "Size 'N' is too small") }
657        assert!(prefix.is_ascii(), "Prefix must be an ASCII character");
658
659        // Allocate the output buffer.
660        let mut __raw = [0u8; N];
661
662        // Prepend the prefix character.
663        __raw[0] = prefix as u8;
664
665        // Encode the input to the buffer.
666        let __pos = __internal::en(src, 0, M, &mut __raw, 1, None) + 1;
667
668        Self::new(__raw, __pos)
669    }
670
671    /// Encodes a byte array with a prefix into a [`Buffer`].
672    ///
673    /// # Examples
674    ///
675    /// ```rust
676    /// # use c32::Error;
677    /// use c32::Buffer;
678    ///
679    /// let input = [42, 42, 42];
680    /// let en = Buffer::<6, true>::try_encode(&input, 'S')?;
681    /// assert_eq!(en.as_str(), "S2MAHA");
682    /// # Ok::<(), Error>(())
683    /// ```
684    #[inline]
685    pub const fn try_encode<const M: usize>(
686        src: &[u8; M],
687        prefix: char,
688    ) -> Result<Self> {
689        const { assert!(N > encoded_len(M), "Size 'N' is too small") }
690
691        // Assert that the prefix is ASCII.
692        if !prefix.is_ascii() {
693            return Err(Error::InvalidCharacter {
694                char: prefix,
695                index: 0,
696            });
697        }
698
699        Ok(Self::encode(src, prefix))
700    }
701
702    /// Decodes a slice of prefixed encoded bytes into a [`Buffer`].
703    ///
704    /// # Examples
705    ///
706    /// ```rust
707    /// use c32::Buffer;
708    ///
709    /// const INPUT: [u8; 6] = *b"S2MAHA";
710    /// const DE: Buffer<6, true> = Buffer::<6, true>::decode(&INPUT, 'S');
711    /// assert_eq!(DE.as_bytes(), [42, 42, 42]);
712    /// ```
713    #[inline]
714    #[must_use]
715    pub const fn decode(src: &[u8], prefix: char) -> Self {
716        assert!(N >= decoded_len(src.len() - 1), "Size 'N' is too small");
717        assert!(prefix.is_ascii(), "Prefix must be an ASCII character");
718        assert!(!src.is_empty(), "Input must contain min. 1 character");
719        assert!(src[0] == prefix as u8, "Input must start with prefix");
720
721        // Allocate the output buffer.
722        let mut __raw = [0u8; N];
723
724        // Decode the input (without prefix) to the buffer.
725        let __pos = match __internal::de(src, 1, src.len() - 1, &mut __raw, 0) {
726            Ok(pos) => pos,
727            Err(Error::InvalidCharacter { char: _, index: _ }) => {
728                panic!("Input contains invalid characters")
729            }
730            _ => unreachable!(),
731        };
732
733        Self::new(__raw, __pos)
734    }
735
736    /// Decodes a slice of prefixed encoded bytes into a [`Buffer`].
737    ///
738    /// # Examples
739    ///
740    /// ```rust
741    /// # use c32::Error;
742    /// use c32::Buffer;
743    ///
744    /// let input = b"S2MAHA";
745    /// let de = Buffer::<6, true>::try_decode(input, 'S')?;
746    /// assert_eq!(de.as_bytes(), [42, 42, 42]);
747    /// # Ok::<(), Error>(())
748    /// ```
749    #[inline]
750    pub const fn try_decode(src: &[u8], prefix: char) -> Result<Self> {
751        // Assert that the input is not empty.
752        if src.is_empty() {
753            return Err(Error::MissingPrefix {
754                char: prefix,
755                got: None,
756            });
757        }
758
759        // Assert that the prefix is ASCII.
760        if !prefix.is_ascii() {
761            return Err(Error::InvalidCharacter {
762                char: prefix,
763                index: 0,
764            });
765        }
766
767        // Assert that the string starts with the prefix.
768        if src[0] != prefix as u8 {
769            return Err(Error::MissingPrefix {
770                char: prefix,
771                got: Some(src[0] as char),
772            });
773        }
774
775        // Assert that the buffer has enough capacity.
776        let capacity = decoded_len(src.len() - 1); // Always 1 for ASCII prefixes
777        if N < capacity {
778            return Err(Error::BufferTooSmall {
779                min: capacity,
780                len: N,
781            });
782        }
783
784        // Allocate the output buffer
785        let mut __raw = [0u8; N];
786
787        // Decode the input into the buffer.
788        let __pos = match __internal::de(src, 1, src.len() - 1, &mut __raw, 0) {
789            Ok(pos) => pos,
790            Err(Error::InvalidCharacter { char, index }) => {
791                return Err(Error::InvalidCharacter {
792                    char,
793                    index: index + 1,
794                });
795            }
796            Err(e) => return Err(e),
797        };
798
799        Ok(Self::new(__raw, __pos))
800    }
801}
802
803#[cfg(feature = "check")]
804impl<const N: usize> Buffer<N, false, en::Check> {
805    /// Encodes a byte array with a checksum into a [`Buffer`].
806    ///
807    /// # Examples
808    ///
809    /// ```rust,no_fmt
810    /// use c32::en::Check;
811    /// use c32::Buffer;
812    ///
813    /// const INPUT: [u8; 3] = [42, 42, 42];
814    /// const EN: Buffer<13, false, Check> = Buffer::<13, false, Check>::encode(&INPUT, 0);
815    /// assert_eq!(EN.as_str(), "0AHA59B9201Z");
816    /// ```
817    #[inline]
818    #[must_use]
819    pub const fn encode<const M: usize>(src: &[u8; M], version: u8) -> Self {
820        const { assert!(N >= encoded_check_len(M), "Size 'N' is too small") }
821        assert!(version < 32, "Version must be < 32");
822
823        // Allocate the output buffer.
824        let mut __raw = [0u8; N];
825
826        // Prepend the version character.
827        __raw[0] = ALPHABET[version as usize];
828
829        // Compute the checksum.
830        let sum = checksum::compute(src, version);
831
832        // Encode the input and checksum to the buffer.
833        let __pos = __internal::en(src, 0, M, &mut __raw, 1, Some(sum)) + 1;
834
835        Self::new(__raw, __pos)
836    }
837
838    /// Encodes a byte array with a checksum into a [`Buffer`].
839    ///
840    /// # Examples
841    ///
842    /// ```rust
843    /// use c32::en::Check;
844    /// use c32::Buffer;
845    /// use c32::Error;
846    ///
847    /// let input = [42, 42, 42];
848    /// let en = Buffer::<13, false, Check>::try_encode(&input, 0)?;
849    /// assert_eq!(en.as_str(), "0AHA59B9201Z");
850    /// # Ok::<(), Error>(())
851    /// ```
852    #[inline]
853    pub const fn try_encode<const M: usize>(
854        src: &[u8; M],
855        version: u8,
856    ) -> Result<Self> {
857        const { assert!(N >= encoded_check_len(M), "Size 'N' is too small") }
858
859        // Assert that the version is valid (< 32).
860        if version >= 32 {
861            return Err(Error::InvalidVersion {
862                expected: "must be < 32",
863                version,
864            });
865        }
866
867        Ok(Self::encode(src, version))
868    }
869
870    /// Decodes a slice of check-encoded bytes into a [`Buffer`].
871    ///
872    /// # Examples
873    ///
874    /// ```rust,no_fmt
875    /// use c32::Buffer;
876    /// use c32::en::Check;
877    ///
878    /// const INPUT: [u8; 12] = *b"0AHA59B9201Z";
879    /// const RESULT: (Buffer<12, false, Check>, u8) = Buffer::<12, false, Check>::decode(&INPUT);
880    /// assert_eq!(RESULT.0.as_bytes(), [42, 42, 42]);
881    /// assert_eq!(RESULT.1, 0);
882    // ```
883    #[inline]
884    #[must_use]
885    pub const fn decode(src: &[u8]) -> (Self, u8) {
886        assert!(N >= decoded_check_len(src.len()), "Size 'N' is too small");
887        assert!(src.len() >= 2, "Input must contain min. 2 characters");
888
889        // Allocate the output buffer.
890        let mut __raw = [0u8; N];
891
892        // Extract the version byte
893        let mut buffer = [0u8; 1];
894        let _ = match __internal::de(&[src[0]], 0, 1, &mut buffer, 0) {
895            Ok(pos) => pos,
896            Err(Error::InvalidCharacter { char: _, index: _ }) => {
897                panic!("Input must not contain invalid characters")
898            }
899            _ => unreachable!(),
900        };
901
902        // Assert that the version is valid (< 32).
903        let version = buffer[0];
904        assert!(version < 32, "Version must be < 32");
905
906        // Decode the remaining bytes into the output buffer.
907        let __pos = match __internal::de(src, 1, src.len() - 1, &mut __raw, 0) {
908            Ok(pos) => pos,
909            Err(Error::InvalidCharacter { char: _, index: _ }) => {
910                panic!("Input must not contain invalid characters")
911            }
912            _ => unreachable!(),
913        };
914
915        let __pos = __pos - checksum::BYTE_LENGTH;
916
917        // Extract the checksum.
918        let mut sum = [0u8; checksum::BYTE_LENGTH];
919        __internal::memcpy(&mut sum, 0, &__raw, __pos, checksum::BYTE_LENGTH);
920
921        // Compute the expected checksum.
922        let mut expected = [0u8; checksum::BYTE_LENGTH];
923        __internal::memcpy(&mut expected, 0, &__raw, __pos, 4);
924
925        // Assert that the computed and actual checksums match.
926        assert!(__internal::memcmp(&expected, &sum, 4), "Checksum mismatch");
927
928        (Self::new(__raw, __pos), version)
929    }
930
931    /// Decodes a slice of check-encoded bytes into a [`Buffer`].
932    ///
933    /// # Examples
934    ///
935    /// ```rust
936    /// # use c32::Error;
937    /// use c32::en::Check;
938    /// use c32::Buffer;
939    ///
940    /// let input = b"0AHA59B9201Z";
941    /// let (de, version) = Buffer::<12, false, Check>::try_decode(input)?;
942    /// assert_eq!(de.as_bytes(), [42, 42, 42]);
943    /// assert_eq!(version, 0);
944    /// # Ok::<(), Error>(())
945    /// ```
946    #[inline]
947    pub const fn try_decode(src: &[u8]) -> Result<(Self, u8)> {
948        // Assert that the buffer has enough capacity.
949        let capacity = decoded_check_len(src.len());
950        if N < capacity {
951            return Err(Error::BufferTooSmall {
952                min: capacity,
953                len: N,
954            });
955        }
956
957        // Assert that the input bytes contain the minimum amount.
958        if src.len() < 2 {
959            return Err(Error::InsufficientData {
960                min: 2,
961                len: src.len(),
962            });
963        }
964
965        // Allocate the output buffer.
966        let mut __raw = [0u8; N];
967
968        // Extract the version byte
969        let mut buffer = [0u8; 1];
970        let _ = match __internal::de(&[src[0]], 0, 1, &mut buffer, 0) {
971            Ok(pos) => pos,
972            Err(err) => return Err(err),
973        };
974
975        // Assert that the version is valid (< 32).
976        let version = buffer[0];
977        if version >= 32 {
978            return Err(Error::InvalidVersion {
979                expected: "must be < 32",
980                version,
981            });
982        }
983
984        // Decode the remaining bytes into the output buffer.
985        let __pos = match __internal::de(src, 1, src.len() - 1, &mut __raw, 0) {
986            Ok(pos) => pos,
987            Err(Error::InvalidCharacter { char, index }) => {
988                return Err(Error::InvalidCharacter {
989                    char,
990                    index: index + 1,
991                });
992            }
993            Err(e) => return Err(e),
994        };
995
996        let __pos = __pos - checksum::BYTE_LENGTH;
997
998        // Extract the checksum.
999        let mut sum = [0u8; checksum::BYTE_LENGTH];
1000        __internal::memcpy(&mut sum, 0, &__raw, __pos, checksum::BYTE_LENGTH);
1001
1002        // Compute the expected checksum.
1003        let mut expected = [0u8; checksum::BYTE_LENGTH];
1004        __internal::memcpy(&mut expected, 0, &__raw, __pos, 4);
1005
1006        // Assert that the computed and actual checksums match.
1007        if !__internal::memcmp(&expected, &sum, checksum::BYTE_LENGTH) {
1008            return Err(Error::ChecksumMismatch { expected, got: sum });
1009        }
1010
1011        Ok((Self::new(__raw, __pos), version))
1012    }
1013}
1014
1015#[cfg(feature = "check")]
1016impl<const N: usize> Buffer<N, true, en::Check> {
1017    /// Encodes a byte array with a checksum and prefix into a [`Buffer`].
1018    ///
1019    ///
1020    /// # Examples
1021    ///
1022    /// ```rust,no_fmt
1023    /// use c32::en::Check;
1024    /// use c32::Buffer;
1025    ///
1026    /// const INPUT: [u8; 3] = [42, 42, 42];
1027    /// const EN: Buffer<14, true, Check> = Buffer::<14, true, Check>::encode(&INPUT, 'S', 0);
1028    /// assert_eq!(EN.as_str(), "S0AHA59B9201Z");
1029    /// ```
1030    #[inline]
1031    #[must_use]
1032    pub const fn encode<const M: usize>(
1033        src: &[u8; M],
1034        prefix: char,
1035        version: u8,
1036    ) -> Self {
1037        const { assert!(N > encoded_check_len(M), "Size 'N' is too small") }
1038        assert!(version < 32, "Version must be < 32");
1039
1040        // Allocate the output buffer.
1041        let mut __raw = [0u8; N];
1042
1043        // Prepend the prefix character.
1044        __raw[0] = prefix as u8;
1045
1046        // Prepend the version character.
1047        __raw[1] = ALPHABET[version as usize];
1048
1049        // Compute the checksum.
1050        let sum = checksum::compute(src, version);
1051
1052        // Encode the input and checksum to the buffer.
1053        let __pos = __internal::en(src, 0, M, &mut __raw, 2, Some(sum)) + 2;
1054
1055        Self::new(__raw, __pos)
1056    }
1057
1058    /// Encodes a byte array with a checksum and prefix into a [`Buffer`].
1059    ///
1060    /// # Examples
1061    ///
1062    /// ```rust
1063    /// # use c32::Error;
1064    /// use c32::en::Check;
1065    /// use c32::Buffer;
1066    ///
1067    /// let input = [42, 42, 42];
1068    /// let en = Buffer::<14, true, Check>::try_encode(&input, 'S', 0)?;
1069    /// assert_eq!(en.as_str(), "S0AHA59B9201Z");
1070    /// # Ok::<(), Error>(())
1071    /// ```
1072    #[inline]
1073    pub const fn try_encode<const M: usize>(
1074        src: &[u8; M],
1075        prefix: char,
1076        version: u8,
1077    ) -> Result<Self> {
1078        const { assert!(N > encoded_check_len(M), "Size 'N' is too small") }
1079
1080        // Assert that the version is valid (< 32).
1081        if version >= 32 {
1082            return Err(Error::InvalidVersion {
1083                expected: "must be < 32",
1084                version,
1085            });
1086        }
1087
1088        // Allocate the output buffer.
1089        let mut __raw = [0u8; N];
1090
1091        // Prepend the prefix character.
1092        __raw[0] = prefix as u8;
1093
1094        // Prepend the version character.
1095        __raw[1] = ALPHABET[version as usize];
1096
1097        // Compute the checksum.
1098        let sum = checksum::compute(src, version);
1099
1100        // Encode the input and checksum to the buffer.
1101        let __pos = __internal::en(src, 0, M, &mut __raw, 2, Some(sum)) + 2;
1102
1103        Ok(Self::new(__raw, __pos))
1104    }
1105
1106    /// Decodes a slice of prefixed check-encoded bytes into a [`Buffer`].
1107    ///
1108    /// # Examples
1109    ///
1110    /// ```rust,no_fmt
1111    /// use c32::en::Check;
1112    /// use c32::Buffer;
1113    ///
1114    /// const INPUT: [u8; 13] = *b"S0AHA59B9201Z";
1115    /// const RESULT: (Buffer<14, true, Check>, u8) = Buffer::<14, true, Check>::decode(&INPUT, 'S');
1116    /// assert_eq!(RESULT.0.as_bytes(), [42, 42, 42]);
1117    /// assert_eq!(RESULT.1, 0);
1118    /// ```
1119    #[inline]
1120    #[must_use]
1121    pub const fn decode(src: &[u8], prefix: char) -> (Self, u8) {
1122        assert!(N >= decoded_check_len(src.len() - 1), "'N' is too small");
1123        assert!(prefix.is_ascii(), "Prefix must be an ASCII character");
1124        assert!(src.len() >= 3, "Input must contain min. 3 characters");
1125        assert!(src[0] == prefix as u8, "Input must start with prefix");
1126
1127        // Extract the version byte.
1128        let mut buffer = [0u8; 1];
1129        let _ = match __internal::de(&[src[1]], 0, 1, &mut buffer, 0) {
1130            Ok(pos) => pos,
1131            Err(Error::InvalidCharacter { char: _, index: _ }) => {
1132                panic!("Input must not contain invalid characters")
1133            }
1134            _ => unreachable!(),
1135        };
1136
1137        // Assert that the version is < 32.
1138        let version = buffer[0];
1139        assert!(version < 32, "Version must be < 32");
1140
1141        // Allocate the output buffer.
1142        let mut __raw = [0u8; N];
1143
1144        // Decode the payload.
1145        let pos = match __internal::de(src, 2, src.len() - 2, &mut __raw, 0) {
1146            Ok(pos) => pos,
1147            Err(Error::InvalidCharacter { char: _, index: _ }) => {
1148                panic!("Input must not contain invalid characters")
1149            }
1150            _ => unreachable!(),
1151        };
1152
1153        let __pos = pos - checksum::BYTE_LENGTH;
1154
1155        // Extract the checksum.
1156        let mut sum = [0u8; checksum::BYTE_LENGTH];
1157        __internal::memcpy(&mut sum, 0, &__raw, __pos, checksum::BYTE_LENGTH);
1158
1159        // Compute the expected checksum.
1160        let mut expected = [0u8; checksum::BYTE_LENGTH];
1161        __internal::memcpy(&mut expected, 0, &__raw, __pos, 4);
1162
1163        // Assert that the computed and actual checksums match.
1164        assert!(__internal::memcmp(&expected, &sum, 4), "Checksum mismatch");
1165
1166        (Self::new(__raw, __pos), version)
1167    }
1168
1169    /// Decodes a slice of prefixed check-encoded bytes into a [`Buffer`].
1170    ///
1171    /// # Examples
1172    ///
1173    ///  ```rust
1174    /// # use c32::Error;
1175    /// use c32::en::Check;
1176    /// use c32::Buffer;
1177    ///
1178    /// let input = b"S0AHA59B9201Z";
1179    /// let (de, version) = Buffer::<14, true, Check>::try_decode(input, 'S')?;
1180    /// assert_eq!(de.as_bytes(), [42, 42, 42]);
1181    /// assert_eq!(version, 0);
1182    /// # Ok::<(), Error>(())
1183    /// ```
1184    #[inline]
1185    pub const fn try_decode(src: &[u8], prefix: char) -> Result<(Self, u8)> {
1186        // Assert that the buffer has enough capacity.
1187        let capacity = decoded_check_len(src.len() - prefix.len_utf8());
1188        if N < capacity {
1189            return Err(Error::BufferTooSmall {
1190                min: capacity,
1191                len: N,
1192            });
1193        }
1194
1195        // Assert that the prefix is ASCII.
1196        if !prefix.is_ascii() {
1197            return Err(Error::InvalidCharacter {
1198                char: prefix,
1199                index: 0,
1200            });
1201        }
1202
1203        // Assert that the input is not empty.
1204        if src.is_empty() {
1205            return Err(Error::MissingPrefix {
1206                char: prefix,
1207                got: None,
1208            });
1209        }
1210
1211        // Assert that the input has the minimum required length.
1212        if src.len() < 3 {
1213            return Err(Error::InsufficientData {
1214                min: 3,
1215                len: src.len(),
1216            });
1217        }
1218
1219        // Assert that the string starts with the prefix.
1220        if src[0] != prefix as u8 {
1221            return Err(Error::MissingPrefix {
1222                char: prefix,
1223                got: Some(src[0] as char),
1224            });
1225        }
1226
1227        // Extract the version byte
1228        let mut buffer = [0u8; 1];
1229        let _ = match __internal::de(&[src[1]], 0, 1, &mut buffer, 0) {
1230            Ok(pos) => pos,
1231            Err(err) => return Err(err),
1232        };
1233
1234        // Assert that the version is valid (< 32).
1235        let version = buffer[0];
1236        if version >= 32 {
1237            return Err(Error::InvalidVersion {
1238                expected: "must be < 32",
1239                version,
1240            });
1241        }
1242
1243        // Allocate the output buffer.
1244        let mut __raw = [0u8; N];
1245
1246        // Decode the payload into the buffer.
1247        let mut __pos =
1248            match __internal::de(src, 2, src.len() - 2, &mut __raw, 0) {
1249                Ok(pos) => pos,
1250                Err(Error::InvalidCharacter { char, index }) => {
1251                    return Err(Error::InvalidCharacter {
1252                        char,
1253                        index: index + 2,
1254                    });
1255                }
1256                Err(e) => return Err(e),
1257            };
1258
1259        // Extract the checksum.
1260        __pos -= checksum::BYTE_LENGTH;
1261        let mut sum = [0u8; checksum::BYTE_LENGTH];
1262        __internal::memcpy(&mut sum, 0, &__raw, __pos, checksum::BYTE_LENGTH);
1263
1264        // Compute the expected checksum.
1265        let mut expected = [0u8; checksum::BYTE_LENGTH];
1266        __internal::memcpy(&mut expected, 0, &__raw, __pos, 4);
1267
1268        // Assert that the checksums match.
1269        if !__internal::memcmp(&expected, &sum, checksum::BYTE_LENGTH) {
1270            return Err(Error::ChecksumMismatch { expected, got: sum });
1271        }
1272
1273        Ok((Self::new(__raw, __pos), version))
1274    }
1275}
1276
1277/// Computes the required capacity for encoding into Crockford Base32.
1278///
1279/// # Notes
1280///
1281/// The calculation breaks down into:
1282///
1283/// - For every 5 bytes (40 bits), exactly 8 Base32 characters are needed.
1284/// - For remaining bytes, more characters are needed based on the total bits.
1285///
1286/// # Examples
1287///
1288/// ```rust
1289/// assert_eq!(c32::encoded_len(0), 0);
1290/// assert_eq!(c32::encoded_len(1), 2);
1291/// assert_eq!(c32::encoded_len(3), 5);
1292/// ```
1293#[inline]
1294#[must_use]
1295pub const fn encoded_len(n: usize) -> usize {
1296    (n * 8 + 4) / 5
1297}
1298
1299/// Computes the required capacity for encoding into Crockford Base32Check.
1300///
1301/// # Notes
1302///
1303/// The calculation breaks down into:
1304///
1305/// - One byte for the version character.
1306/// - The encoded length of the payload plus a 4-byte checksum.
1307///
1308/// # Examples
1309///
1310/// ```rust
1311/// assert_eq!(c32::encoded_check_len(0), 8);
1312/// assert_eq!(c32::encoded_check_len(1), 9);
1313/// assert_eq!(c32::encoded_check_len(3), 13);
1314/// ```
1315#[inline]
1316#[must_use]
1317#[cfg(feature = "check")]
1318pub const fn encoded_check_len(n: usize) -> usize {
1319    1 + encoded_len(n + 4)
1320}
1321
1322/// Computes the required capacity for decoding from Crockford Base32.
1323///
1324/// # Examples
1325///
1326/// ```rust
1327/// assert_eq!(c32::decoded_len(0), 0);
1328/// assert_eq!(c32::decoded_len(1), 1);
1329/// assert_eq!(c32::decoded_len(2), 2);
1330/// ```
1331#[inline]
1332#[must_use]
1333pub const fn decoded_len(n: usize) -> usize {
1334    n
1335}
1336
1337/// Computes the required capacity for decoding from Crockford Base32Check.
1338///
1339/// # Examples
1340///
1341/// ```rust
1342/// assert_eq!(c32::decoded_check_len(8), 8);
1343/// assert_eq!(c32::decoded_check_len(9), 9);
1344/// assert_eq!(c32::decoded_check_len(13), 13);
1345/// ```
1346#[inline]
1347#[must_use]
1348#[cfg(feature = "check")]
1349pub const fn decoded_check_len(n: usize) -> usize {
1350    n
1351}
1352
1353/// Encodes bytes into a Crockford Base32-encoded string.
1354///
1355/// # Panics
1356///
1357/// This method can panic in two cases:
1358///
1359/// - If encoding fails despitce sufficient buffer capacity.
1360/// - If the encoded output contains non-UTF8 bytes.
1361///
1362/// Both panics should never occur under normal circumstances.
1363///
1364/// # Examples
1365///
1366/// ```rust
1367/// let en = c32::encode([42, 42, 42]);
1368/// assert_eq!(en, "2MAHA");
1369/// ```
1370#[inline]
1371#[must_use]
1372#[cfg(feature = "alloc")]
1373pub fn encode<B>(src: B) -> String
1374where
1375    B: AsRef<[u8]>,
1376{
1377    let src = src.as_ref();
1378
1379    // Allocate the output buffer.
1380    let capacity = encoded_len(src.len());
1381    let mut dst = vec![0u8; capacity];
1382
1383    // This should not panic, as we allocate enough space.
1384    let offset = encode_into(src, &mut dst).unwrap();
1385    dst.truncate(offset);
1386
1387    // This should not panic, as we only push valid ASCII.
1388    String::from_utf8(dst).unwrap()
1389}
1390
1391/// Decodes a Crockford Base32-encoded string.
1392///
1393/// # Errors
1394///
1395/// This method will return an [`Error`] if:
1396///
1397/// - [`Error::InvalidCharacter`], the input contains invalid characters.
1398///
1399/// # Examples
1400///
1401/// ```rust
1402/// # use c32::Error;
1403/// let de = c32::decode("2MAHA")?;
1404/// assert_eq!(de, [42, 42, 42]);
1405/// # Ok::<(), Error>(())
1406/// ```
1407#[inline]
1408#[cfg(feature = "alloc")]
1409pub fn decode(str: &str) -> Result<Vec<u8>> {
1410    let bytes = str.as_bytes();
1411
1412    // Allocate the output buffer.
1413    let capacity = decoded_len(bytes.len());
1414    let mut dst = vec![0u8; capacity];
1415
1416    // Decode the input bytes into the buffer.
1417    let offset = decode_into(bytes, &mut dst)?;
1418    dst.truncate(offset);
1419
1420    Ok(dst)
1421}
1422
1423/// Encodes bytes into a prefixed Crockford Base32-encoded string.
1424///
1425/// # Examples
1426///
1427/// ```rust
1428/// let en = c32::encode_prefixed([42, 42, 42], 'S');
1429/// assert_eq!(en, "S2MAHA");
1430/// ```
1431#[inline]
1432#[must_use]
1433#[cfg(feature = "alloc")]
1434pub fn encode_prefixed<B>(src: B, prefix: char) -> String
1435where
1436    B: AsRef<[u8]>,
1437{
1438    let src = src.as_ref();
1439
1440    // Encode the input bytes.
1441    let encoded = encode(src);
1442
1443    // Allocate the output string.
1444    let capacity = prefix.len_utf8() + encoded.len();
1445    let mut dst = String::with_capacity(capacity);
1446
1447    // Append the prefix and encoded string.
1448    dst.push(prefix);
1449    dst.push_str(&encoded);
1450    dst
1451}
1452
1453/// Decodes a prefixed Crockford Base32-encoded string.
1454///
1455/// # Errors
1456///
1457/// This method will return an [`Error`] if:
1458///
1459/// - [`Error::MissingPrefix`], the input does not start with the prefix.
1460/// - [`Error::InvalidCharacter`], the input contains invalid characters.
1461///
1462/// # Examples
1463///
1464/// ```rust
1465/// # use c32::Error;
1466/// let de = c32::decode_prefixed("P2MAHA", 'P')?;
1467/// assert_eq!(de, [42, 42, 42]);
1468/// # Ok::<(), Error>(())
1469/// ```
1470#[inline]
1471#[cfg(feature = "alloc")]
1472pub fn decode_prefixed(str: &str, prefix: char) -> Result<Vec<u8>> {
1473    // Assert that the string starts with the prefix.
1474    if !str.starts_with(prefix) {
1475        return Err(Error::MissingPrefix {
1476            char: prefix,
1477            got: str.chars().next(),
1478        });
1479    }
1480
1481    // Skip the prefix character and decode the rest.
1482    match decode(&str[prefix.len_utf8()..]) {
1483        Ok(bytes) => Ok(bytes),
1484        Err(Error::InvalidCharacter { char, index }) => {
1485            // This adjusts the index in an 'InvalidCharacter' to account for
1486            // the prefix in the original string that we don't decode.
1487            Err(Error::InvalidCharacter {
1488                char,
1489                index: index + prefix.len_utf8(),
1490            })
1491        }
1492        Err(e) => Err(e),
1493    }
1494}
1495
1496/// Encodes bytes into a Crockford Base32Check-encoded string.
1497///
1498/// # Panics
1499///
1500/// This method can panic in two cases:
1501///
1502/// - If encoding fails despite sufficient buffer capacity.
1503/// - If the encoded output contains non-UTF8 bytes.
1504///
1505/// Both panics should never occur under normal circumstances.
1506///
1507/// # Errors
1508///
1509/// This method will return an [`Error`] if:
1510///
1511/// - [`Error::InvalidVersion`], the version is 32 or greater.
1512///
1513/// # Examples
1514///
1515/// ```rust
1516/// # use c32::Error;
1517/// let en = c32::encode_check([42, 42, 42], 0)?;
1518/// assert_eq!(en, "0AHA59B9201Z");
1519/// # Ok::<(), Error>(())
1520/// ```
1521#[inline]
1522#[cfg(all(feature = "alloc", feature = "check"))]
1523pub fn encode_check<B>(src: B, version: u8) -> Result<String>
1524where
1525    B: AsRef<[u8]>,
1526{
1527    let src = src.as_ref();
1528
1529    // Allocate the output string.
1530    let capacity = encoded_check_len(src.len());
1531    let mut dst = vec![0u8; capacity];
1532
1533    // This should not panic, as we allocate enough space.
1534    let offset = encode_check_into(src, &mut dst, version)?;
1535    dst.truncate(offset);
1536
1537    // This should not panic, as we only push valid ASCII.
1538    Ok(String::from_utf8(dst).unwrap())
1539}
1540
1541/// Decodes a Crockford Base32Check-encoded string.
1542///
1543/// # Errors
1544///
1545/// This method will return an [`Error`] if:
1546///
1547/// - [`Error::InvalidCharacter`], the input contains invalid characters.
1548/// - [`Error::InsufficientData`], the input has fewer bytes than required.
1549/// - [`Error::ChecksumMismatch`], the checksum's do not match.
1550/// - [`Error::InvalidVersion`], the version is 32 or greater.
1551///
1552/// # Examples
1553///
1554/// ```rust
1555/// # use c32::Error;
1556/// let (bytes, version) = c32::decode_check("0AHA59B9201Z")?;
1557/// assert_eq!(bytes, [42, 42, 42]);
1558/// assert_eq!(version, 0);
1559/// # Ok::<(), Error>(())
1560/// ```
1561#[inline]
1562#[cfg(all(feature = "alloc", feature = "check"))]
1563pub fn decode_check(str: &str) -> Result<(Vec<u8>, u8)> {
1564    let bytes = str.as_bytes();
1565
1566    // Allocate the output buffer.
1567    let capacity = decoded_check_len(bytes.len());
1568    let mut dst = vec![0u8; capacity];
1569
1570    // Decode the input bytes into the buffer.
1571    let (offset, version) = decode_check_into(bytes, &mut dst)?;
1572    dst.truncate(offset);
1573
1574    Ok((dst, version))
1575}
1576
1577/// Encodes bytes into a prefixed Crockford Base32Check-encoded string.
1578///
1579/// # Errors
1580///
1581/// This method will return an [`Error`] if:
1582///
1583/// - [`Error::InvalidVersion`], the version is 32 or greater.
1584///
1585/// # Examples
1586///
1587/// ```rust
1588/// # use c32::Error;
1589/// let en = c32::encode_check_prefixed([42, 42, 42], 'S', 0)?;
1590/// assert_eq!(en, "S0AHA59B9201Z");
1591/// # Ok::<(), Error>(())
1592/// ```
1593#[inline]
1594#[cfg(all(feature = "alloc", feature = "check"))]
1595pub fn encode_check_prefixed<B>(
1596    src: B,
1597    prefix: char,
1598    version: u8,
1599) -> Result<String>
1600where
1601    B: AsRef<[u8]>,
1602{
1603    let src = src.as_ref();
1604
1605    // Encode the input bytes.
1606    let encoded = encode_check(src, version)?;
1607
1608    // Allocate the output string.
1609    let capacity = prefix.len_utf8() + encoded.len();
1610    let mut dst = String::with_capacity(capacity);
1611
1612    // Append the prefix and encoded string.
1613    dst.push(prefix);
1614    dst.push_str(&encoded);
1615    Ok(dst)
1616}
1617
1618/// Decodes a prefixed Crockford Base32Check-encoded string.
1619///
1620/// # Errors
1621///
1622/// This method will return an [`Error`] if:
1623///
1624/// - [`Error::MissingPrefix`], the input does not start with the prefix.
1625/// - [`Error::InvalidCharacter`], the input contains invalid characters.
1626/// - [`Error::InsufficientData`], the input has fewer bytes than required.
1627/// - [`Error::InvalidVersion`], the version is 32 or greater.
1628/// - [`Error::ChecksumMismatch`], the checksum's do not match.
1629///
1630/// # Examples
1631///
1632/// ```rust
1633/// # use c32::Error;
1634/// let (bytes, version) = c32::decode_check_prefixed("S0AHA59B9201Z", 'S')?;
1635/// assert_eq!(bytes, [42, 42, 42]);
1636/// assert_eq!(version, 0);
1637/// # Ok::<(), Error>(())
1638/// ```
1639#[inline]
1640#[cfg(all(feature = "alloc", feature = "check"))]
1641pub fn decode_check_prefixed(str: &str, prefix: char) -> Result<(Vec<u8>, u8)> {
1642    // Assert that the string starts with the prefix.
1643    if !str.starts_with(prefix) {
1644        return Err(Error::MissingPrefix {
1645            char: prefix,
1646            got: str.chars().next(),
1647        });
1648    }
1649
1650    // Skip the prefix character and decode the rest.
1651    match decode_check(&str[prefix.len_utf8()..]) {
1652        Ok(bytes) => Ok(bytes),
1653        Err(Error::InvalidCharacter { char, index }) => {
1654            // This adjusts the index in an 'InvalidCharacter' to account for
1655            // the prefix in the original string that we don't decode.
1656            Err(Error::InvalidCharacter {
1657                char,
1658                index: index + prefix.len_utf8(),
1659            })
1660        }
1661        Err(e) => Err(e),
1662    }
1663}
1664
1665/// Encodes bytes as Crockford Base32 into a provided buffer.
1666///
1667/// # Returns
1668///
1669/// The number of bytes written to the output buffer.
1670///
1671/// # Errors
1672///
1673/// This method will return an [`Error`] if:
1674///
1675/// - [`Error::BufferTooSmall`], the output buffer lacks capacity.
1676///
1677/// # Examples
1678///
1679/// ```rust
1680/// # use c32::Error;
1681/// # let mut dst = [0u8; 5];
1682/// let offset = c32::encode_into(&[42, 42, 42], &mut dst)?;
1683/// assert_eq!(&dst[..offset], b"2MAHA");
1684/// assert_eq!(offset, 5);
1685/// # Ok::<(), Error>(())
1686/// ```
1687#[inline]
1688pub fn encode_into(src: &[u8], dst: &mut [u8]) -> Result<usize> {
1689    // Assert that the buffer has enough capacity.
1690    let capacity = encoded_len(src.len());
1691    if dst.len() < capacity {
1692        return Err(Error::BufferTooSmall {
1693            min: capacity,
1694            len: dst.len(),
1695        });
1696    }
1697
1698    // Encode the input bytes, and return the amount of bytes written.
1699    let offset = __internal::en(src, 0, src.len(), &mut dst[..], 0, None);
1700    Ok(offset)
1701}
1702
1703/// Decodes Crockford Base32-encoded bytes into a provided buffer.
1704///
1705/// # Returns
1706///
1707/// The number of bytes written to the output buffer.
1708///
1709/// # Errors
1710///
1711/// This method will return an [`Error`] if:
1712///
1713/// - [`Error::BufferTooSmall`], the output buffer lacks capacity.
1714/// - [`Error::InvalidCharacter`], the input contains invalid characters.
1715///
1716/// # Examples
1717///
1718/// ```rust
1719/// # use c32::Error;
1720/// # let mut dst = [0u8; 5];
1721/// let offset = c32::decode_into(b"2MAHA", &mut dst)?;
1722/// assert_eq!(&dst[..offset], [42, 42, 42]);
1723/// assert_eq!(offset, 3);
1724/// # Ok::<(), Error>(())
1725/// ```
1726#[inline]
1727pub fn decode_into(src: &[u8], dst: &mut [u8]) -> Result<usize> {
1728    // Assert that the buffer has enough capacity.
1729    let capacity = decoded_len(src.len());
1730    if dst.len() < capacity {
1731        return Err(Error::BufferTooSmall {
1732            min: capacity,
1733            len: dst.len(),
1734        });
1735    }
1736
1737    // Encode the input bytes, and return the amount of bytes written.
1738    __internal::de(src, 0, src.len(), dst, 0)
1739}
1740
1741/// Encodes bytes as Crockford Base32Check into a provided buffer.
1742///
1743/// # Returns
1744///
1745/// The number of bytes written to the output buffer.
1746///
1747/// # Errors
1748///
1749/// This method will return an [`Error`] if:
1750///
1751/// - [`Error::BufferTooSmall`], the output buffer lacks capacity.
1752/// - [`Error::InvalidVersion`], the version is 32 or greater.
1753///
1754/// # Examples
1755///
1756/// ```rust
1757/// # use c32::Error;
1758/// # let mut dst = [0u8; 13];
1759/// let offset = c32::encode_check_into(&[42, 42, 42], &mut dst, 0)?;
1760/// assert_eq!(&dst[..offset], b"0AHA59B9201Z");
1761/// assert_eq!(offset, 12);
1762/// # Ok::<(), Error>(())
1763/// ```
1764#[inline]
1765#[cfg(feature = "check")]
1766pub fn encode_check_into(
1767    src: &[u8],
1768    dst: &mut [u8],
1769    version: u8,
1770) -> Result<usize> {
1771    // Assert that the buffer has enough capacity.
1772    let capacity = encoded_check_len(src.len());
1773    if dst.len() < capacity {
1774        return Err(Error::BufferTooSmall {
1775            min: capacity,
1776            len: dst.len(),
1777        });
1778    }
1779
1780    // Assert that the version is valid (< 32).
1781    if version >= 32 {
1782        return Err(Error::InvalidVersion {
1783            expected: "must be < 32",
1784            version,
1785        });
1786    }
1787
1788    // Insert the version character into the output buffer.
1789    let mut offset = 0;
1790    dst[offset] = ALPHABET[version as usize];
1791    offset += 1;
1792
1793    // Compute the checksum for the input bytes and version.
1794    let sum = checksum::compute(src, version);
1795
1796    // Encode the bytes and checksum.
1797    offset +=
1798        __internal::en(src, 0, src.len(), &mut dst[offset..], 0, Some(sum));
1799
1800    Ok(offset)
1801}
1802
1803/// Decodes Crockford Base32Check-encoded bytes into a provided buffer.
1804///
1805/// # Returns
1806///
1807/// The number of bytes written to the output buffer and the version.
1808///
1809/// # Errors
1810///
1811/// This method will return an [`Error`] if:
1812///
1813/// - [`Error::InvalidCharacter`], the input contains invalid characters.
1814/// - [`Error::InsufficientData`], the input has fewer bytes than required.
1815/// - [`Error::InvalidVersion`], the version is 32 or greater.
1816/// - [`Error::ChecksumMismatch`], the checksum's do not match.
1817/// - [`Error::BufferTooSmall`], the output buffer lacks capacity.
1818///
1819/// # Examples
1820///
1821/// ```rust
1822/// # use c32::Error;
1823/// # let mut dst = [0u8; 12];
1824/// let (offset, version) = c32::decode_check_into(b"0AHA59B9201Z", &mut dst)?;
1825/// assert_eq!(&dst[..offset], [42, 42, 42]);
1826/// assert_eq!(version, 0);
1827/// # Ok::<(), Error>(())
1828/// ```
1829#[inline]
1830#[cfg(feature = "check")]
1831#[allow(clippy::missing_panics_doc)]
1832pub fn decode_check_into(src: &[u8], dst: &mut [u8]) -> Result<(usize, u8)> {
1833    // Assert that the buffer has enough capacity.
1834    let capacity = decoded_check_len(src.len());
1835    if dst.len() < capacity {
1836        return Err(Error::BufferTooSmall {
1837            min: capacity,
1838            len: dst.len(),
1839        });
1840    }
1841
1842    // Assert that the input bytes contain the minimum amount.
1843    if src.len() < 2 {
1844        return Err(Error::InsufficientData {
1845            min: 2,
1846            len: src.len(),
1847        });
1848    }
1849
1850    // This should not panic, as the check above ensures enough bytes.
1851    let (tag, payload) = src.split_first().unwrap();
1852
1853    // Decode the version byte.
1854    let mut buffer = [0u8; 1];
1855    let _ = __internal::de(&[*tag], 0, 1, &mut buffer, 0)?;
1856    let version = buffer[0];
1857
1858    // Assert that the recovered version is valid. (< 32).
1859    if version >= 32 {
1860        return Err(Error::InvalidVersion {
1861            expected: "must be < 32",
1862            version,
1863        });
1864    }
1865
1866    // Decode the remaining bytes into the output buffer.
1867    let mut offset = match __internal::de(payload, 0, payload.len(), dst, 0) {
1868        Ok(pos) => pos,
1869        Err(Error::InvalidCharacter { char, index }) => {
1870            return Err(Error::InvalidCharacter {
1871                char,
1872                index: index + 1,
1873            });
1874        }
1875        Err(e) => return Err(e),
1876    };
1877
1878    // Extract the checksum.
1879    offset -= checksum::BYTE_LENGTH;
1880    let sum =
1881        checksum::from_slice(&dst[offset..offset + checksum::BYTE_LENGTH]);
1882
1883    // Compute the expected checksum.
1884    let expected = checksum::compute(&dst[..offset], version);
1885
1886    // Assert that the computed and actual checksums match.
1887    if !__internal::memcmp(&expected, &sum, checksum::BYTE_LENGTH) {
1888        return Err(Error::ChecksumMismatch { expected, got: sum });
1889    }
1890
1891    Ok((offset, version))
1892}
1893
1894/// Private module containing internal methods.
1895#[allow(dead_code)]
1896mod __internal {
1897    use super::*;
1898
1899    /// Encodes a byte slice into Crockford Base32.
1900    ///
1901    /// # Notes
1902    ///
1903    /// - The output buffer must be properly sized.
1904    #[inline]
1905    #[must_use]
1906    pub(crate) const fn en(
1907        src: &[u8],
1908        src_offset: usize,
1909        src_len: usize,
1910        dst: &mut [u8],
1911        dst_offset: usize,
1912        checksum: Option<[u8; 4]>,
1913    ) -> usize {
1914        const MASK_5: u16 = 0x1F;
1915        const SHIFT_5: u16 = 5;
1916
1917        let mut carry = 0;
1918        let mut carry_bits = 0;
1919        let mut dst_pos = dst_offset;
1920
1921        // count leading zeros
1922        let mut leading_zeros = 0;
1923        while leading_zeros < src_len && src[src_offset + leading_zeros] == 0 {
1924            leading_zeros += 1;
1925        }
1926
1927        // process checksum if provided
1928        if let Some(sum) = checksum {
1929            let mut checksum_pos = sum.len();
1930            while checksum_pos > 0 {
1931                checksum_pos -= 1;
1932
1933                // accumulate bits into carry
1934                carry |= (sum[checksum_pos] as u16) << carry_bits;
1935                carry_bits += 8;
1936
1937                // extract 5-bit chunks
1938                while carry_bits >= SHIFT_5 {
1939                    // write character from chunk
1940                    dst[dst_pos] = ALPHABET[(carry & MASK_5) as usize];
1941                    dst_pos += 1;
1942
1943                    // shift out processed bytes
1944                    carry >>= SHIFT_5;
1945                    carry_bits -= SHIFT_5;
1946                }
1947            }
1948        }
1949
1950        // process bytes in reverse
1951        let mut input_pos = src_offset + src_len;
1952        while input_pos > src_offset {
1953            input_pos -= 1;
1954
1955            // accumulate bits into carry
1956            carry |= (src[input_pos] as u16) << carry_bits;
1957            carry_bits += 8;
1958
1959            // extract 5-bit chunks
1960            while carry_bits >= SHIFT_5 {
1961                // write character from chunk
1962                dst[dst_pos] = ALPHABET[(carry & MASK_5) as usize];
1963                dst_pos += 1;
1964
1965                // shift out processed bits
1966                carry >>= SHIFT_5;
1967                carry_bits -= SHIFT_5;
1968            }
1969        }
1970
1971        // process remaining bits
1972        if carry_bits > 0 && carry > 0 {
1973            dst[dst_pos] = ALPHABET[(carry & MASK_5) as usize];
1974            dst_pos += 1;
1975        }
1976
1977        // truncate trailing zeros
1978        while dst_pos > dst_offset && dst[dst_pos - 1] == ALPHABET[0] {
1979            dst_pos -= 1;
1980        }
1981
1982        // restore leading zeros from input
1983        let mut j = 0;
1984        while j < leading_zeros {
1985            dst[dst_pos] = ALPHABET[0];
1986            dst_pos += 1;
1987            j += 1;
1988        }
1989
1990        // reverse the buffer
1991        if dst_pos > dst_offset {
1992            let mut lhs = dst_offset;
1993            let mut rhs = dst_pos - 1;
1994            while lhs < rhs {
1995                let temp = dst[lhs];
1996                dst[lhs] = dst[rhs];
1997                dst[rhs] = temp;
1998                lhs += 1;
1999                rhs -= 1;
2000            }
2001        }
2002
2003        dst_pos - dst_offset
2004    }
2005
2006    /// Decodes Crockford Base32-encoded bytes.
2007    ///
2008    /// # Notes
2009    ///
2010    /// - The output buffer must be properly sized.
2011    #[inline]
2012    #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
2013    pub(crate) const fn de(
2014        src: &[u8],
2015        src_offset: usize,
2016        src_len: usize,
2017        dst: &mut [u8],
2018        dst_offset: usize,
2019    ) -> Result<usize> {
2020        const MASK_8: u16 = 0xFF;
2021        const SHIFT_8: u16 = 8;
2022
2023        let mut carry = 0;
2024        let mut carry_bits = 0;
2025        let mut dst_pos = dst_offset;
2026
2027        // count leading zeros
2028        let mut leading_zeros = 0;
2029        while leading_zeros < src_len
2030            && src[src_offset + leading_zeros] == ALPHABET[0]
2031        {
2032            leading_zeros += 1;
2033        }
2034
2035        // process characters in reverse
2036        let mut input_pos = src_offset + src_len;
2037        while input_pos > src_offset {
2038            input_pos -= 1;
2039
2040            // fetch the byte
2041            let byte = src[input_pos];
2042            if byte >= 128 {
2043                return Err(Error::InvalidCharacter {
2044                    char: byte as char,
2045                    index: input_pos - src_offset,
2046                });
2047            }
2048
2049            // convert the byte to a map index
2050            let index = BYTE_MAP[byte as usize];
2051            if index < 0 {
2052                return Err(Error::InvalidCharacter {
2053                    char: byte as char,
2054                    index: input_pos - src_offset,
2055                });
2056            }
2057
2058            // accumulate bits into carry
2059            carry |= (index as u16) << carry_bits;
2060            carry_bits += 5;
2061
2062            // extract 8-bit chunks
2063            while carry_bits >= SHIFT_8 {
2064                // write byte from chunk
2065                dst[dst_pos] = (carry & MASK_8) as u8;
2066                dst_pos += 1;
2067
2068                // shift out processed bits
2069                carry >>= SHIFT_8;
2070                carry_bits -= SHIFT_8;
2071            }
2072        }
2073
2074        // process remaining bits
2075        if carry_bits > 0 {
2076            dst[dst_pos] = carry as u8;
2077            dst_pos += 1;
2078        }
2079
2080        // truncate trailing zeros
2081        while dst_pos > dst_offset && dst[dst_pos - 1] == 0 {
2082            dst_pos -= 1;
2083        }
2084
2085        // restore leading zeros from input
2086        let mut j = 0;
2087        while j < leading_zeros {
2088            dst[dst_pos] = 0;
2089            dst_pos += 1;
2090            j += 1;
2091        }
2092
2093        // reverse the buffer
2094        if dst_pos > dst_offset {
2095            let mut lhs = dst_offset;
2096            let mut rhs = dst_pos - 1;
2097            while lhs < rhs {
2098                let temp = dst[lhs];
2099                dst[lhs] = dst[rhs];
2100                dst[rhs] = temp;
2101                lhs += 1;
2102                rhs -= 1;
2103            }
2104        }
2105
2106        Ok(dst_pos - dst_offset)
2107    }
2108
2109    /// Copies `n` bytes from `src` to `dst`.
2110    ///
2111    /// # Notes
2112    ///
2113    /// - Both slices must be properly sized.
2114    #[inline]
2115    pub(crate) const fn memcpy(
2116        dst: &mut [u8],
2117        dst_offset: usize,
2118        src: &[u8],
2119        src_offset: usize,
2120        n: usize,
2121    ) {
2122        let mut i = 0;
2123        while i < n {
2124            dst[dst_offset + i] = src[src_offset + i];
2125            i += 1;
2126        }
2127    }
2128
2129    /// Compares `n` bytes between `lhs` and `rhs`.
2130    ///
2131    /// # Notes
2132    ///
2133    /// - Both slices must be properly sized.
2134    #[inline]
2135    #[must_use]
2136    pub(crate) const fn memcmp(lhs: &[u8], rhs: &[u8], n: usize) -> bool {
2137        let mut i = 0;
2138        while i < n {
2139            if lhs[i] != rhs[i] {
2140                return false;
2141            }
2142            i += 1;
2143        }
2144        true
2145    }
2146}