1#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5pub enum EncodeError {
6 LengthOverflow,
8 InvalidAlphabet,
15 BackendMismatch,
22 InvalidLineWrap {
24 line_len: usize,
26 },
27 InputTooLarge {
29 input_len: usize,
31 buffer_len: usize,
33 },
34 OutputTooSmall {
36 required: usize,
38 available: usize,
40 },
41}
42
43impl core::fmt::Display for EncodeError {
44 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
45 match self {
46 Self::LengthOverflow => f.write_str("base64 output length overflows usize"),
47 Self::InvalidAlphabet => f.write_str("base64 alphabet produced non-ASCII output"),
48 Self::BackendMismatch => {
49 f.write_str("base64 accelerated backend diverged from scalar output")
50 }
51 Self::InvalidLineWrap { line_len } => {
52 write!(f, "base64 line wrap length {line_len} is invalid")
53 }
54 Self::InputTooLarge {
55 input_len,
56 buffer_len,
57 } => write!(
58 f,
59 "base64 input length {input_len} exceeds buffer length {buffer_len}"
60 ),
61 Self::OutputTooSmall {
62 required,
63 available,
64 } => write!(
65 f,
66 "base64 output buffer too small: required {required}, available {available}"
67 ),
68 }
69 }
70}
71
72#[cfg(feature = "std")]
73impl std::error::Error for EncodeError {}
74
75#[derive(Clone, Copy, Eq, PartialEq)]
93pub enum DecodeError {
94 InvalidInput,
97 InvalidLength,
99 InvalidByte {
101 index: usize,
103 byte: u8,
105 },
106 InvalidPadding {
108 index: usize,
110 },
111 InvalidLineWrap {
113 index: usize,
115 },
116 OutputTooSmall {
118 required: usize,
120 available: usize,
122 },
123 StagingTooSmall {
125 required: usize,
127 available: usize,
129 },
130}
131
132impl core::fmt::Debug for DecodeError {
133 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
134 f.debug_struct("DecodeError")
135 .field("kind", &self.kind())
136 .finish_non_exhaustive()
137 }
138}
139
140#[derive(Clone, Copy, Debug, Eq, PartialEq)]
145#[non_exhaustive]
146pub enum DecodeErrorKind {
147 InvalidInput,
150 InvalidLength,
152 InvalidByte,
154 InvalidPadding,
156 InvalidLineWrap,
158 OutputTooSmall,
160 StagingTooSmall,
162}
163
164impl DecodeErrorKind {
165 #[must_use]
167 pub const fn as_str(self) -> &'static str {
168 match self {
169 Self::InvalidInput => "invalid-input",
170 Self::InvalidLength => "invalid-length",
171 Self::InvalidByte => "invalid-byte",
172 Self::InvalidPadding => "invalid-padding",
173 Self::InvalidLineWrap => "invalid-line-wrap",
174 Self::OutputTooSmall => "output-too-small",
175 Self::StagingTooSmall => "staging-too-small",
176 }
177 }
178}
179
180impl core::fmt::Display for DecodeErrorKind {
181 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
182 f.write_str(self.as_str())
183 }
184}
185
186impl core::fmt::Display for DecodeError {
187 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
188 match self {
189 Self::InvalidInput => f.write_str("malformed base64 input"),
190 Self::InvalidLength => f.write_str("invalid base64 input length"),
191 Self::InvalidByte { index, byte } => {
192 write!(f, "invalid base64 byte 0x{byte:02x} at index {index}")
193 }
194 Self::InvalidPadding { index } => write!(f, "invalid base64 padding at index {index}"),
195 Self::InvalidLineWrap { index } => {
196 write!(f, "invalid base64 line wrapping at index {index}")
197 }
198 Self::OutputTooSmall {
199 required,
200 available,
201 } => write!(
202 f,
203 "base64 decode output buffer too small: required {required}, available {available}"
204 ),
205 Self::StagingTooSmall {
206 required,
207 available,
208 } => write!(
209 f,
210 "base64 decode staging buffer too small: required {required}, available {available}"
211 ),
212 }
213 }
214}
215
216impl DecodeError {
217 #[must_use]
224 pub const fn kind(self) -> DecodeErrorKind {
225 match self {
226 Self::InvalidInput => DecodeErrorKind::InvalidInput,
227 Self::InvalidLength => DecodeErrorKind::InvalidLength,
228 Self::InvalidByte { .. } => DecodeErrorKind::InvalidByte,
229 Self::InvalidPadding { .. } => DecodeErrorKind::InvalidPadding,
230 Self::InvalidLineWrap { .. } => DecodeErrorKind::InvalidLineWrap,
231 Self::OutputTooSmall { .. } => DecodeErrorKind::OutputTooSmall,
232 Self::StagingTooSmall { .. } => DecodeErrorKind::StagingTooSmall,
233 }
234 }
235
236 pub(crate) fn with_index_offset(self, offset: usize) -> Self {
237 match self {
238 Self::InvalidByte { index, byte } => Self::InvalidByte {
239 index: index.saturating_add(offset),
240 byte,
241 },
242 Self::InvalidPadding { index } => Self::InvalidPadding {
243 index: index.saturating_add(offset),
244 },
245 Self::InvalidLineWrap { index } => Self::InvalidLineWrap {
246 index: index.saturating_add(offset),
247 },
248 Self::InvalidInput
249 | Self::InvalidLength
250 | Self::OutputTooSmall { .. }
251 | Self::StagingTooSmall { .. } => self,
252 }
253 }
254
255 pub(crate) fn with_index_map(self, indexes: &[usize]) -> Self {
256 match self {
257 Self::InvalidByte { index, byte } => Self::InvalidByte {
258 index: indexes.get(index).copied().unwrap_or(index),
259 byte,
260 },
261 Self::InvalidPadding { index } => Self::InvalidPadding {
262 index: indexes.get(index).copied().unwrap_or(index),
263 },
264 Self::InvalidLineWrap { index } => Self::InvalidLineWrap {
265 index: indexes.get(index).copied().unwrap_or(index),
266 },
267 Self::InvalidInput
268 | Self::InvalidLength
269 | Self::OutputTooSmall { .. }
270 | Self::StagingTooSmall { .. } => self,
271 }
272 }
273}
274
275#[cfg(feature = "std")]
276impl std::error::Error for DecodeError {}
277
278#[cfg(test)]
279mod tests {
280 use super::DecodeError;
281
282 #[test]
283 fn index_offsets_saturate_on_overflow() {
284 assert_eq!(
285 DecodeError::InvalidByte {
286 index: 7,
287 byte: b'$'
288 }
289 .with_index_offset(usize::MAX),
290 DecodeError::InvalidByte {
291 index: usize::MAX,
292 byte: b'$'
293 }
294 );
295 assert_eq!(
296 DecodeError::InvalidPadding { index: 7 }.with_index_offset(usize::MAX),
297 DecodeError::InvalidPadding { index: usize::MAX }
298 );
299 assert_eq!(
300 DecodeError::InvalidLineWrap { index: 7 }.with_index_offset(usize::MAX),
301 DecodeError::InvalidLineWrap { index: usize::MAX }
302 );
303 }
304
305 #[cfg(feature = "alloc")]
306 #[test]
307 fn debug_redacts_input_derived_details() {
308 let error = DecodeError::InvalidByte {
309 index: 42,
310 byte: b'$',
311 };
312 let rendered = alloc::format!("{error:?}");
313 assert!(rendered.contains("InvalidByte"));
314 assert!(!rendered.contains("42"));
315 assert!(!rendered.contains("24"));
316 }
317}