#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum EncodeError {
LengthOverflow,
InvalidLineWrap {
line_len: usize,
},
InputTooLarge {
input_len: usize,
buffer_len: usize,
},
OutputTooSmall {
required: usize,
available: usize,
},
}
impl core::fmt::Display for EncodeError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::LengthOverflow => f.write_str("base64 output length overflows usize"),
Self::InvalidLineWrap { line_len } => {
write!(f, "base64 line wrap length {line_len} is invalid")
}
Self::InputTooLarge {
input_len,
buffer_len,
} => write!(
f,
"base64 input length {input_len} exceeds buffer length {buffer_len}"
),
Self::OutputTooSmall {
required,
available,
} => write!(
f,
"base64 output buffer too small: required {required}, available {available}"
),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for EncodeError {}
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum DecodeError {
InvalidInput,
InvalidLength,
InvalidByte {
index: usize,
byte: u8,
},
InvalidPadding {
index: usize,
},
InvalidLineWrap {
index: usize,
},
OutputTooSmall {
required: usize,
available: usize,
},
StagingTooSmall {
required: usize,
available: usize,
},
}
impl core::fmt::Debug for DecodeError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("DecodeError")
.field("kind", &self.kind())
.finish_non_exhaustive()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum DecodeErrorKind {
InvalidInput,
InvalidLength,
InvalidByte,
InvalidPadding,
InvalidLineWrap,
OutputTooSmall,
StagingTooSmall,
}
impl DecodeErrorKind {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::InvalidInput => "invalid-input",
Self::InvalidLength => "invalid-length",
Self::InvalidByte => "invalid-byte",
Self::InvalidPadding => "invalid-padding",
Self::InvalidLineWrap => "invalid-line-wrap",
Self::OutputTooSmall => "output-too-small",
Self::StagingTooSmall => "staging-too-small",
}
}
}
impl core::fmt::Display for DecodeErrorKind {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(self.as_str())
}
}
impl core::fmt::Display for DecodeError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::InvalidInput => f.write_str("malformed base64 input"),
Self::InvalidLength => f.write_str("invalid base64 input length"),
Self::InvalidByte { index, byte } => {
write!(f, "invalid base64 byte 0x{byte:02x} at index {index}")
}
Self::InvalidPadding { index } => write!(f, "invalid base64 padding at index {index}"),
Self::InvalidLineWrap { index } => {
write!(f, "invalid base64 line wrapping at index {index}")
}
Self::OutputTooSmall {
required,
available,
} => write!(
f,
"base64 decode output buffer too small: required {required}, available {available}"
),
Self::StagingTooSmall {
required,
available,
} => write!(
f,
"base64 decode staging buffer too small: required {required}, available {available}"
),
}
}
}
impl DecodeError {
#[must_use]
pub const fn kind(self) -> DecodeErrorKind {
match self {
Self::InvalidInput => DecodeErrorKind::InvalidInput,
Self::InvalidLength => DecodeErrorKind::InvalidLength,
Self::InvalidByte { .. } => DecodeErrorKind::InvalidByte,
Self::InvalidPadding { .. } => DecodeErrorKind::InvalidPadding,
Self::InvalidLineWrap { .. } => DecodeErrorKind::InvalidLineWrap,
Self::OutputTooSmall { .. } => DecodeErrorKind::OutputTooSmall,
Self::StagingTooSmall { .. } => DecodeErrorKind::StagingTooSmall,
}
}
pub(crate) fn with_index_offset(self, offset: usize) -> Self {
match self {
Self::InvalidByte { index, byte } => Self::InvalidByte {
index: index.saturating_add(offset),
byte,
},
Self::InvalidPadding { index } => Self::InvalidPadding {
index: index.saturating_add(offset),
},
Self::InvalidLineWrap { index } => Self::InvalidLineWrap {
index: index.saturating_add(offset),
},
Self::InvalidInput
| Self::InvalidLength
| Self::OutputTooSmall { .. }
| Self::StagingTooSmall { .. } => self,
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for DecodeError {}
#[cfg(test)]
mod tests {
use super::DecodeError;
#[test]
fn index_offsets_saturate_on_overflow() {
assert_eq!(
DecodeError::InvalidByte {
index: 7,
byte: b'$'
}
.with_index_offset(usize::MAX),
DecodeError::InvalidByte {
index: usize::MAX,
byte: b'$'
}
);
assert_eq!(
DecodeError::InvalidPadding { index: 7 }.with_index_offset(usize::MAX),
DecodeError::InvalidPadding { index: usize::MAX }
);
assert_eq!(
DecodeError::InvalidLineWrap { index: 7 }.with_index_offset(usize::MAX),
DecodeError::InvalidLineWrap { index: usize::MAX }
);
}
#[cfg(feature = "alloc")]
#[test]
fn debug_redacts_input_derived_details() {
let error = DecodeError::InvalidByte {
index: 42,
byte: b'$',
};
let rendered = alloc::format!("{error:?}");
assert!(rendered.contains("InvalidByte"));
assert!(!rendered.contains("42"));
assert!(!rendered.contains("24"));
}
}