base64-ng 2.0.0

no_std-first Base64 encoding and decoding with strict APIs and a security-heavy release process
Documentation
//! Ordinary bounded stack storage.

use super::{
    ordinary::OneShotError,
    specifications::{Base64, Codec},
};

/// Error returned when a visible prefix exceeds its backing array.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct BufferLengthError {
    length: usize,
    capacity: usize,
}

impl BufferLengthError {
    pub(super) const fn new(length: usize, capacity: usize) -> Self {
        Self { length, capacity }
    }

    /// Returns the rejected visible length.
    #[must_use]
    pub const fn length(self) -> usize {
        self.length
    }

    /// Returns the backing array capacity.
    #[must_use]
    pub const fn capacity(self) -> usize {
        self.capacity
    }
}

impl core::fmt::Display for BufferLengthError {
    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(
            formatter,
            "visible buffer length {} exceeds capacity {}",
            self.length, self.capacity
        )
    }
}

#[cfg(feature = "std")]
impl std::error::Error for BufferLengthError {}

/// Ordinary bounded bytes intended to contain encoded text.
///
/// This ordinary value is `Copy`, has visible formatting, and performs no
/// drop-time cleanup. Use `secret::SecretArray` for secret-bearing storage.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct EncodedArray<const CAP: usize> {
    bytes: [u8; CAP],
    len: usize,
}

/// Ordinary bounded decoded bytes.
///
/// This ordinary value is `Copy` and performs no drop-time cleanup. Its name
/// describes transform direction, not secrecy.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct DecodedArray<const CAP: usize> {
    bytes: [u8; CAP],
    len: usize,
}

macro_rules! ordinary_array {
    ($name:ident, $description:literal) => {
        impl<const CAP: usize> $name<CAP> {
            #[doc = $description]
            pub const fn from_array(
                bytes: [u8; CAP],
                len: usize,
            ) -> Result<Self, BufferLengthError> {
                if len > CAP {
                    Err(BufferLengthError::new(len, CAP))
                } else {
                    Ok(Self { bytes, len })
                }
            }

            pub(crate) const fn from_initialized(
                bytes: [u8; CAP],
                len: usize,
            ) -> Result<Self, BufferLengthError> {
                Self::from_array(bytes, len)
            }

            /// Returns the initialized visible prefix.
            #[must_use]
            pub fn as_bytes(&self) -> &[u8] {
                &self.bytes[..self.len]
            }

            /// Returns the initialized prefix length.
            #[must_use]
            pub const fn len(&self) -> usize {
                self.len
            }

            /// Returns whether the initialized prefix is empty.
            #[must_use]
            pub const fn is_empty(&self) -> bool {
                self.len == 0
            }

            /// Returns the fixed backing capacity.
            #[must_use]
            pub const fn capacity(&self) -> usize {
                CAP
            }

            /// Returns the unused backing capacity.
            #[must_use]
            pub const fn remaining_capacity(&self) -> usize {
                CAP - self.len
            }

            /// Consumes the wrapper and returns its backing array and length.
            #[must_use]
            pub const fn into_parts(self) -> ([u8; CAP], usize) {
                (self.bytes, self.len)
            }
        }
    };
}

ordinary_array!(
    EncodedArray,
    "Constructs ordinary encoded storage with a checked visible prefix."
);
ordinary_array!(
    DecodedArray,
    "Constructs ordinary decoded storage with a checked visible prefix."
);

impl<S: Codec> Base64<S> {
    /// Encodes into a bounded ordinary stack array.
    pub fn encode_bounded<const CAP: usize>(
        &self,
        input: &[u8],
    ) -> Result<EncodedArray<CAP>, OneShotError> {
        let mut bytes = [0u8; CAP];
        let len = self.encode_into(input, &mut bytes)?;
        EncodedArray::from_initialized(bytes, len)
            .map_err(|_| OneShotError::Backend(super::contracts::BackendFault::ImpossibleState))
    }

    /// Decodes into a bounded ordinary stack array transactionally.
    pub fn decode_bounded<const CAP: usize>(
        &self,
        input: &[u8],
    ) -> Result<DecodedArray<CAP>, OneShotError> {
        let mut bytes = [0u8; CAP];
        let len = self.decode_into(input, &mut bytes)?;
        DecodedArray::from_initialized(bytes, len)
            .map_err(|_| OneShotError::Backend(super::contracts::BackendFault::ImpossibleState))
    }
}