Coverage Report

Created: 2026-10-28 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/base64-0.23.1/src/decode.rs
Line
Count
Source
1
use crate::engine::{general_purpose::STANDARD, DecodeEstimate, Engine};
2
#[cfg(any(feature = "alloc", test))]
3
use alloc::vec::Vec;
4
use core::fmt;
5
#[cfg(any(feature = "std", test))]
6
use std::error;
7
8
/// Errors that can occur while decoding.
9
#[derive(Clone, PartialEq, Eq)]
10
pub enum DecodeError {
11
    /// An invalid byte was found in the input. The offset and offending byte are provided.
12
    ///
13
    /// Padding characters (`=`) interspersed in the encoded form are invalid, as they may only
14
    /// be present as the last 0-2 bytes of input.
15
    ///
16
    /// This error may also indicate that extraneous trailing input bytes are present, causing
17
    /// otherwise valid padding to no longer be the last bytes of input.
18
    InvalidByte(usize, u8),
19
    /// The length of the input, as measured in valid base64 symbols, is invalid.
20
    /// There must be 2-4 symbols in the last input quad.
21
    InvalidLength(usize),
22
    /// The last non-padding input symbol's encoded 6 bits have nonzero bits that will be discarded.
23
    /// This is indicative of corrupted or truncated Base64.
24
    /// Unlike [`DecodeError::InvalidByte`], which reports symbols that aren't in the alphabet,
25
    /// this error is for symbols that are in the alphabet but represent nonsensical encodings.
26
    ///
27
    /// See [`crate::engine::GeneralPurposeConfig::with_decode_allow_trailing_bits`] to control
28
    /// whether to detect this encoding error and produce this variant.
29
    InvalidLastSymbol {
30
        /// Offset in the input
31
        offset: usize,
32
        /// The offending symbol
33
        symbol: u8,
34
        /// The bits the symbol corresponds to.
35
        ///
36
        /// Since this error is being reported, this value has high bits erroneously set.
37
        /// For a 2-symbol suffix, only the first 2 bits may be set (6 + 2 = 8 bits,
38
        /// 1 byte), and for a 3 symbol, only the first 4 (6 + 6 + 4 = 16, 2 bytes).
39
        symbol_value: u8,
40
    },
41
    /// The nature of the padding was not as configured: absent or incorrect when it must be
42
    /// canonical, or present when it must be absent, etc.
43
    InvalidPadding,
44
}
45
46
impl fmt::Display for DecodeError {
47
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
48
0
        match *self {
49
0
            Self::InvalidByte(index, byte) => {
50
0
                write!(f, "Invalid symbol {}, offset {}.", byte, index)
51
            }
52
0
            Self::InvalidLength(len) => write!(f, "Invalid input length: {}", len),
53
            Self::InvalidLastSymbol {
54
0
                offset,
55
0
                symbol,
56
0
                symbol_value,
57
            } => {
58
0
                write!(
59
0
                    f,
60
                    "Invalid last symbol {:#4x} ('{}') at offset {}, decoded as {:#010b}.",
61
                    symbol,
62
                    // To have been decoded at all, it must have been ascii, but rather than have a
63
                    // panicking code path, replacement char seems reasonable.
64
                    // Can't use `char::from_u32` as that's 1.52+, so we make a 1-byte str.
65
0
                    core::str::from_utf8(&[symbol])
66
0
                        .ok()
67
0
                        .and_then(|s| s.chars().next())
68
                        // associated const is also 1.52+
69
0
                        .unwrap_or(core::char::REPLACEMENT_CHARACTER),
70
                    offset,
71
                    symbol_value
72
                )
73
            }
74
0
            Self::InvalidPadding => write!(f, "Invalid padding"),
75
        }
76
0
    }
77
}
78
79
impl fmt::Debug for DecodeError {
80
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81
        // 1.48.0 can't handle {self}
82
0
        write!(f, "{}", self)
83
0
    }
84
}
85
86
#[cfg(any(feature = "std", test))]
87
impl error::Error for DecodeError {}
88
89
/// Errors that can occur while decoding into a slice.
90
#[derive(Clone, Debug, PartialEq, Eq)]
91
pub enum DecodeSliceError {
92
    /// A [`DecodeError`] occurred
93
    DecodeError(DecodeError),
94
    /// The provided slice is too small.
95
    OutputSliceTooSmall,
96
}
97
98
impl fmt::Display for DecodeSliceError {
99
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100
0
        match self {
101
0
            Self::DecodeError(e) => write!(f, "DecodeError: {}", e),
102
0
            Self::OutputSliceTooSmall => write!(f, "Output slice too small"),
103
        }
104
0
    }
105
}
106
107
#[cfg(any(feature = "std", test))]
108
impl error::Error for DecodeSliceError {
109
0
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
110
0
        match self {
111
0
            DecodeSliceError::DecodeError(e) => Some(e),
112
0
            DecodeSliceError::OutputSliceTooSmall => None,
113
        }
114
0
    }
115
}
116
117
impl From<DecodeError> for DecodeSliceError {
118
0
    fn from(e: DecodeError) -> Self {
119
0
        DecodeSliceError::DecodeError(e)
120
0
    }
121
}
122
123
/// Decode base64 using the [`STANDARD` engine](STANDARD).
124
///
125
/// See [`Engine::decode`].
126
#[deprecated(since = "0.21.0", note = "Use Engine::decode")]
127
#[cfg(any(feature = "alloc", test))]
128
0
pub fn decode<T: AsRef<[u8]>>(input: T) -> Result<Vec<u8>, DecodeError> {
129
0
    STANDARD.decode(input)
130
0
}
131
132
/// Decode from string reference as octets using the specified [Engine].
133
///
134
/// See [`Engine::decode`].
135
///Returns a `Result` containing a `Vec<u8>`.
136
#[deprecated(since = "0.21.0", note = "Use Engine::decode")]
137
#[cfg(any(feature = "alloc", test))]
138
0
pub fn decode_engine<E: Engine, T: AsRef<[u8]>>(
139
0
    input: T,
140
0
    engine: &E,
141
0
) -> Result<Vec<u8>, DecodeError> {
142
0
    engine.decode(input)
143
0
}
144
145
/// Decode from string reference as octets.
146
///
147
/// See [`Engine::decode_vec`].
148
#[cfg(any(feature = "alloc", test))]
149
#[deprecated(since = "0.21.0", note = "Use Engine::decode_vec")]
150
0
pub fn decode_engine_vec<E: Engine, T: AsRef<[u8]>>(
151
0
    input: T,
152
0
    buffer: &mut Vec<u8>,
153
0
    engine: &E,
154
0
) -> Result<(), DecodeError> {
155
0
    engine.decode_vec(input, buffer)
156
0
}
157
158
/// Decode the input into the provided output slice.
159
///
160
/// See [`Engine::decode_slice`].
161
#[deprecated(since = "0.21.0", note = "Use Engine::decode_slice")]
162
0
pub fn decode_engine_slice<E: Engine, T: AsRef<[u8]>>(
163
0
    input: T,
164
0
    output: &mut [u8],
165
0
    engine: &E,
166
0
) -> Result<usize, DecodeSliceError> {
167
0
    engine.decode_slice(input, output)
168
0
}
169
170
/// Returns a conservative estimate of the decoded size of `encoded_len` base64 symbols (rounded up
171
/// to the next group of 3 decoded bytes).
172
///
173
/// The resulting length will be a safe choice for the size of a decode buffer, but may have up to
174
/// 2 trailing bytes that won't end up being needed.
175
///
176
/// # Examples
177
///
178
/// ```
179
/// use base64::decoded_len_estimate;
180
///
181
/// assert_eq!(3, decoded_len_estimate(1));
182
/// assert_eq!(3, decoded_len_estimate(2));
183
/// assert_eq!(3, decoded_len_estimate(3));
184
/// assert_eq!(3, decoded_len_estimate(4));
185
/// // start of the next quad of encoded symbols
186
/// assert_eq!(6, decoded_len_estimate(5));
187
/// ```
188
#[must_use]
189
0
pub fn decoded_len_estimate(encoded_len: usize) -> usize {
190
0
    STANDARD
191
0
        .internal_decoded_len_estimate(encoded_len)
192
0
        .decoded_len_estimate()
193
0
}
194
195
#[cfg(test)]
196
mod tests {
197
    use super::*;
198
    use crate::{
199
        alphabet,
200
        engine::{general_purpose, GeneralPurpose},
201
        tests::{assert_encode_sanity, random_engine},
202
    };
203
    use rand::distr::{Distribution, Uniform};
204
    use rand::{rngs, RngExt};
205
206
    #[test]
207
    fn decode_into_nonempty_vec_doesnt_clobber_existing_prefix() {
208
        let mut orig_data = Vec::new();
209
        let mut encoded_data = String::new();
210
        let mut decoded_with_prefix = Vec::new();
211
        let mut decoded_without_prefix = Vec::new();
212
        let mut prefix = Vec::new();
213
214
        let prefix_len_range = Uniform::new(0, 1000).unwrap();
215
        let input_len_range = Uniform::new(0, 1000).unwrap();
216
217
        let mut rng = rand::make_rng::<rngs::SmallRng>();
218
219
        for _ in 0..10_000 {
220
            orig_data.clear();
221
            encoded_data.clear();
222
            decoded_with_prefix.clear();
223
            decoded_without_prefix.clear();
224
            prefix.clear();
225
226
            let input_len = input_len_range.sample(&mut rng);
227
228
            for _ in 0..input_len {
229
                orig_data.push(rng.random());
230
            }
231
232
            let engine = random_engine(&mut rng);
233
            engine.encode_string(&orig_data, &mut encoded_data);
234
            assert_encode_sanity(&encoded_data, &engine, input_len);
235
236
            let prefix_len = prefix_len_range.sample(&mut rng);
237
238
            // fill the buf with a prefix
239
            for _ in 0..prefix_len {
240
                prefix.push(rng.random());
241
            }
242
243
            decoded_with_prefix.resize(prefix_len, 0);
244
            decoded_with_prefix.copy_from_slice(&prefix);
245
246
            // decode into the non-empty buf
247
            engine
248
                .decode_vec(&encoded_data, &mut decoded_with_prefix)
249
                .unwrap();
250
            // also decode into the empty buf
251
            engine
252
                .decode_vec(&encoded_data, &mut decoded_without_prefix)
253
                .unwrap();
254
255
            assert_eq!(
256
                prefix_len + decoded_without_prefix.len(),
257
                decoded_with_prefix.len()
258
            );
259
            assert_eq!(orig_data, decoded_without_prefix);
260
261
            // append plain decode onto prefix
262
            prefix.append(&mut decoded_without_prefix);
263
264
            assert_eq!(prefix, decoded_with_prefix);
265
        }
266
    }
267
268
    #[test]
269
    fn decode_slice_doesnt_clobber_existing_prefix_or_suffix() {
270
        do_decode_slice_doesnt_clobber_existing_prefix_or_suffix(|e, input, output| {
271
            e.decode_slice(input, output).unwrap()
272
        })
273
    }
274
275
    #[test]
276
    fn decode_slice_unchecked_doesnt_clobber_existing_prefix_or_suffix() {
277
        do_decode_slice_doesnt_clobber_existing_prefix_or_suffix(|e, input, output| {
278
            e.decode_slice_unchecked(input, output).unwrap()
279
        })
280
    }
281
282
    #[test]
283
    fn decode_engine_estimation_works_for_various_lengths() {
284
        let engine = GeneralPurpose::new(&alphabet::STANDARD, general_purpose::NO_PAD);
285
        for num_prefix_quads in 0..100 {
286
            for suffix in &["AA", "AAA", "AAAA"] {
287
                let mut prefix = "AAAA".repeat(num_prefix_quads);
288
                prefix.push_str(suffix);
289
                // make sure no overflow (and thus a panic) occurs
290
                let res = engine.decode(prefix);
291
                assert!(res.is_ok());
292
            }
293
        }
294
    }
295
296
    #[test]
297
    fn decode_slice_output_length_errors() {
298
        for num_quads in 1..100 {
299
            let input = "AAAA".repeat(num_quads);
300
            let mut vec = vec![0; (num_quads - 1) * 3];
301
            assert_eq!(
302
                DecodeSliceError::OutputSliceTooSmall,
303
                STANDARD.decode_slice(&input, &mut vec).unwrap_err()
304
            );
305
            vec.push(0);
306
            assert_eq!(
307
                DecodeSliceError::OutputSliceTooSmall,
308
                STANDARD.decode_slice(&input, &mut vec).unwrap_err()
309
            );
310
            vec.push(0);
311
            assert_eq!(
312
                DecodeSliceError::OutputSliceTooSmall,
313
                STANDARD.decode_slice(&input, &mut vec).unwrap_err()
314
            );
315
            vec.push(0);
316
            // now it works
317
            assert_eq!(
318
                num_quads * 3,
319
                STANDARD.decode_slice(&input, &mut vec).unwrap()
320
            );
321
        }
322
    }
323
324
    #[test]
325
    fn invalid_last_symbol_debug() {
326
        let err = DecodeError::InvalidLastSymbol {
327
            offset: 100,
328
            symbol: b'W',
329
            symbol_value: 0x16,
330
        };
331
332
        assert_eq!(
333
            "Invalid last symbol 0x57 ('W') at offset 100, decoded as 0b00010110.",
334
            format!("{:?}", err)
335
        );
336
    }
337
338
    fn do_decode_slice_doesnt_clobber_existing_prefix_or_suffix<
339
        F: Fn(&GeneralPurpose, &[u8], &mut [u8]) -> usize,
340
    >(
341
        call_decode: F,
342
    ) {
343
        let mut orig_data = Vec::new();
344
        let mut encoded_data = String::new();
345
        let mut decode_buf = Vec::new();
346
        let mut decode_buf_copy: Vec<u8> = Vec::new();
347
348
        let input_len_range = Uniform::new(0, 1000).unwrap();
349
350
        let mut rng = rand::make_rng::<rngs::SmallRng>();
351
352
        for _ in 0..10_000 {
353
            orig_data.clear();
354
            encoded_data.clear();
355
            decode_buf.clear();
356
            decode_buf_copy.clear();
357
358
            let input_len = input_len_range.sample(&mut rng);
359
360
            for _ in 0..input_len {
361
                orig_data.push(rng.random());
362
            }
363
364
            let engine = random_engine(&mut rng);
365
            engine.encode_string(&orig_data, &mut encoded_data);
366
            assert_encode_sanity(&encoded_data, &engine, input_len);
367
368
            // fill the buffer with random garbage, long enough to have some room before and after
369
            for _ in 0..5000 {
370
                decode_buf.push(rng.random());
371
            }
372
373
            // keep a copy for later comparison
374
            decode_buf_copy.extend(decode_buf.iter());
375
376
            let offset = 1000;
377
378
            // decode into the non-empty buf
379
            let decode_bytes_written =
380
                call_decode(&engine, encoded_data.as_bytes(), &mut decode_buf[offset..]);
381
382
            assert_eq!(orig_data.len(), decode_bytes_written);
383
            assert_eq!(
384
                orig_data,
385
                &decode_buf[offset..(offset + decode_bytes_written)]
386
            );
387
            assert_eq!(&decode_buf_copy[0..offset], &decode_buf[0..offset]);
388
            assert_eq!(
389
                &decode_buf_copy[offset + decode_bytes_written..],
390
                &decode_buf[offset + decode_bytes_written..]
391
            );
392
        }
393
    }
394
}
395
396
#[allow(deprecated)]
397
#[cfg(test)]
398
mod coverage_gaming {
399
    use super::*;
400
    use std::error::Error;
401
402
    #[test]
403
    fn decode_error() {
404
        let _ = format!("{:?}", DecodeError::InvalidPadding.clone());
405
        let _ = format!(
406
            "{} {} {} {}",
407
            DecodeError::InvalidByte(0, 0),
408
            DecodeError::InvalidLength(0),
409
            DecodeError::InvalidLastSymbol {
410
                offset: 0,
411
                symbol: 0,
412
                symbol_value: 0,
413
            },
414
            DecodeError::InvalidPadding,
415
        );
416
    }
417
418
    #[test]
419
    fn decode_slice_error() {
420
        let _ = format!("{:?}", DecodeSliceError::OutputSliceTooSmall.clone());
421
        let _ = format!(
422
            "{} {}",
423
            DecodeSliceError::OutputSliceTooSmall,
424
            DecodeSliceError::DecodeError(DecodeError::InvalidPadding)
425
        );
426
        let _ = DecodeSliceError::OutputSliceTooSmall.source();
427
        let _ = DecodeSliceError::DecodeError(DecodeError::InvalidPadding).source();
428
    }
429
430
    #[test]
431
    fn deprecated_fns() {
432
        let _ = decode("");
433
        let _ = decode_engine("", &crate::prelude::BASE64_STANDARD);
434
        let _ = decode_engine_vec("", &mut Vec::new(), &crate::prelude::BASE64_STANDARD);
435
        let _ = decode_engine_slice("", &mut [], &crate::prelude::BASE64_STANDARD);
436
    }
437
438
    #[test]
439
    fn decoded_len_est() {
440
        assert_eq!(3, decoded_len_estimate(4));
441
    }
442
}