Coverage Report

Created: 2026-11-12 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/base64-0.23.0/src/engine/mod.rs
Line
Count
Source
1
//! Provides the [Engine] abstraction and out of the box implementations.
2
use crate::alphabet::Symbol;
3
#[cfg(any(feature = "alloc", test))]
4
use crate::chunked_encoder;
5
use crate::{
6
    encode::{encode_with_padding, EncodeSliceError},
7
    encoded_len, DecodeError, DecodeSliceError,
8
};
9
#[cfg(any(feature = "alloc", test))]
10
use alloc::vec::Vec;
11
12
#[cfg(any(feature = "alloc", test))]
13
use alloc::{string::String, vec};
14
15
pub mod general_purpose;
16
17
#[cfg(all(
18
    feature = "simd-unsafe",
19
    any(
20
        target_arch = "x86_64",
21
        all(target_arch = "aarch64", target_feature = "neon")
22
    )
23
))]
24
pub mod simd;
25
26
#[cfg(test)]
27
mod naive;
28
29
#[cfg(test)]
30
mod tests;
31
32
pub use general_purpose::{GeneralPurpose, GeneralPurposeConfig, Scalar};
33
34
/// The runtime-detected SIMD engine. Requires the `simd-unsafe` feature.
35
#[cfg(all(
36
    feature = "simd-unsafe",
37
    feature = "std",
38
    any(
39
        target_arch = "x86_64",
40
        all(target_arch = "aarch64", target_feature = "neon")
41
    )
42
))]
43
pub use simd::Simd;
44
45
/// The AVX2 engine. Requires the `simd-unsafe` feature on an `x86_64` target.
46
#[cfg(all(feature = "simd-unsafe", target_arch = "x86_64"))]
47
pub use simd::Avx2;
48
49
/// The NEON engine. Requires the `simd-unsafe` feature on an `aarch64` target.
50
#[cfg(all(
51
    feature = "simd-unsafe",
52
    target_arch = "aarch64",
53
    target_feature = "neon"
54
))]
55
pub use simd::Neon;
56
57
/// An `Engine` provides low-level encoding and decoding operations that all other higher-level parts of the API use. Users of the library will generally not need to implement this.
58
///
59
/// Different implementations offer different characteristics. The library currently ships with
60
/// [`GeneralPurpose`] that offers good speed and works on any CPU, with more choices
61
/// coming later, like a constant-time one when side channel resistance is called for, and vendor-specific vectorized ones for more speed.
62
///
63
/// See [`general_purpose::STANDARD_NO_PAD`] if you just want standard base64. Otherwise, when possible, it's
64
/// recommended to store the engine in a `const` so that references to it won't pose any lifetime
65
/// issues, and to avoid repeating the cost of engine setup.
66
///
67
/// Since almost nobody will need to implement `Engine`, docs for internal methods are hidden.
68
// When adding an implementation of Engine, include them in the engine test suite:
69
// - add an implementation of [engine::tests::EngineWrapper]
70
// - add the implementation to the `all_engines` macro
71
// All tests run on all engines listed in the macro.
72
pub trait Engine: Send + Sync {
73
    /// The config type used by this engine
74
    type Config: Config;
75
    /// The decode estimate used by this engine
76
    type DecodeEstimate: DecodeEstimate;
77
78
    /// This is not meant to be called directly; it is only for `Engine` implementors.
79
    /// See the other `encode*` functions on this trait.
80
    ///
81
    /// Encode the `input` bytes into the `output` buffer based on the mapping in `encode_table`.
82
    ///
83
    /// `output` will be long enough to hold the encoded data.
84
    ///
85
    /// Returns the number of bytes written.
86
    ///
87
    /// No padding should be written; that is handled separately.
88
    ///
89
    /// Must not write any bytes into the output slice other than the encoded data.
90
    #[doc(hidden)]
91
    fn internal_encode(&self, input: &[u8], output: &mut [u8]) -> usize;
92
93
    /// This is not meant to be called directly; it is only for `Engine` implementors.
94
    ///
95
    /// As an optimization to prevent the decoded length from being calculated twice, it is
96
    /// sometimes helpful to have a conservative estimate of the decoded size before doing the
97
    /// decoding, so this calculation is done separately and passed to [Engine::decode()] as needed.
98
    #[doc(hidden)]
99
    fn internal_decoded_len_estimate(&self, input_len: usize) -> Self::DecodeEstimate;
100
101
    /// This is not meant to be called directly; it is only for `Engine` implementors.
102
    /// See the other `decode*` functions on this trait.
103
    ///
104
    /// Decode `input` base64 bytes into the `output` buffer.
105
    ///
106
    /// `decode_estimate` is the result of [Engine::internal_decoded_len_estimate()], which is passed in to avoid
107
    /// calculating it again (expensive on short inputs).`
108
    ///
109
    /// Each complete 4-byte chunk of encoded data decodes to 3 bytes of decoded data, but this
110
    /// function must also handle the final possibly partial chunk.
111
    /// If the input length is not a multiple of 4, or uses padding bytes to reach a multiple of 4,
112
    /// the trailing 2 or 3 bytes must decode to 1 or 2 bytes, respectively, as per the
113
    /// [RFC](https://tools.ietf.org/html/rfc4648#section-3.5).
114
    ///
115
    /// Decoding must not write any bytes into the output slice other than the decoded data.
116
    ///
117
    /// Non-canonical trailing bits in the final symbols or non-canonical padding must be reported as
118
    /// errors unless the engine is configured otherwise.
119
    #[doc(hidden)]
120
    fn internal_decode(
121
        &self,
122
        input: &[u8],
123
        output: &mut [u8],
124
        decode_estimate: Self::DecodeEstimate,
125
    ) -> Result<DecodeMetadata, DecodeSliceError>;
126
127
    /// Returns the config for this engine.
128
    fn config(&self) -> &Self::Config;
129
130
    /// Encode arbitrary octets as base64 using the provided `Engine`.
131
    /// Returns a `String`.
132
    ///
133
    /// # Example
134
    ///
135
    /// ```rust
136
    /// use base64::{Engine as _, engine::{self, general_purpose}, alphabet};
137
    ///
138
    /// let b64 = general_purpose::STANDARD.encode(b"hello world~");
139
    /// println!("{}", b64);
140
    ///
141
    /// const CUSTOM_ENGINE: engine::GeneralPurpose =
142
    ///     engine::GeneralPurpose::new(&alphabet::URL_SAFE, general_purpose::NO_PAD);
143
    ///
144
    /// let b64_url = CUSTOM_ENGINE.encode(b"hello internet~");
145
    /// ```
146
    #[cfg(any(feature = "alloc", test))]
147
    #[inline]
148
0
    fn encode<T: AsRef<[u8]>>(&self, input: T) -> String {
149
0
        fn inner<E>(engine: &E, input_bytes: &[u8]) -> String
150
0
        where
151
0
            E: Engine + ?Sized,
152
        {
153
0
            let encoded_size = encoded_len(input_bytes.len(), engine.config().encode_padding())
154
0
                .expect("integer overflow when calculating buffer size");
155
156
0
            let mut buf = vec![0; encoded_size];
157
158
0
            encode_with_padding(input_bytes, &mut buf[..], engine, encoded_size);
159
160
0
            String::from_utf8(buf).expect("Invalid UTF8")
161
0
        }
Unexecuted instantiation: base64::engine::Engine::encode::inner::<base64::engine::general_purpose::GeneralPurpose>
Unexecuted instantiation: base64::engine::Engine::encode::inner::<_>
162
163
0
        inner(self, input.as_ref())
164
0
    }
Unexecuted instantiation: <base64::engine::general_purpose::GeneralPurpose as base64::engine::Engine>::encode::<hybrid_array::Array<u8, typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UTerm, typenum::bit::B1>, typenum::bit::B0>, typenum::bit::B0>, typenum::bit::B0>, typenum::bit::B0>>>
Unexecuted instantiation: <base64::engine::general_purpose::GeneralPurpose as base64::engine::Engine>::encode::<alloc::string::String>
Unexecuted instantiation: <_ as base64::engine::Engine>::encode::<_>
165
166
    /// Encode arbitrary octets as base64 into a supplied `String`.
167
    /// Writes into the supplied `String`, which may allocate if its internal buffer isn't big enough.
168
    ///
169
    /// # Example
170
    ///
171
    /// ```rust
172
    /// use base64::{Engine as _, engine::{self, general_purpose}, alphabet};
173
    /// const CUSTOM_ENGINE: engine::GeneralPurpose =
174
    ///     engine::GeneralPurpose::new(&alphabet::URL_SAFE, general_purpose::NO_PAD);
175
    ///
176
    /// fn main() {
177
    ///     let mut buf = String::new();
178
    ///     general_purpose::STANDARD.encode_string(b"hello world~", &mut buf);
179
    ///     println!("{}", buf);
180
    ///
181
    ///     buf.clear();
182
    ///     CUSTOM_ENGINE.encode_string(b"hello internet~", &mut buf);
183
    ///     println!("{}", buf);
184
    /// }
185
    /// ```
186
    #[cfg(any(feature = "alloc", test))]
187
    #[inline]
188
0
    fn encode_string<T: AsRef<[u8]>>(&self, input: T, output_buf: &mut String) {
189
0
        fn inner<E>(engine: &E, input_bytes: &[u8], output_buf: &mut String)
190
0
        where
191
0
            E: Engine + ?Sized,
192
        {
193
0
            let mut sink = chunked_encoder::StringSink::new(output_buf);
194
195
0
            chunked_encoder::ChunkedEncoder::new(engine)
196
0
                .encode(input_bytes, &mut sink)
197
0
                .expect("Writing to a String shouldn't fail");
198
0
        }
199
200
0
        inner(self, input.as_ref(), output_buf);
201
0
    }
202
203
    /// Encode arbitrary octets as base64 into a supplied slice.
204
    /// Writes into the supplied output buffer.
205
    ///
206
    /// This is useful if you wish to avoid allocation entirely (e.g. encoding into a stack-resident
207
    /// or statically-allocated buffer).
208
    ///
209
    /// # Example
210
    ///
211
    #[cfg_attr(feature = "alloc", doc = "```")]
212
    #[cfg_attr(not(feature = "alloc"), doc = "```ignore")]
213
    /// use base64::{Engine as _, engine::general_purpose};
214
    /// let s = b"hello internet!";
215
    /// let mut buf = Vec::new();
216
    /// // make sure we'll have a slice big enough for base64 + padding
217
    /// buf.resize(s.len() * 4 / 3 + 4, 0);
218
    ///
219
    /// let bytes_written = general_purpose::STANDARD.encode_slice(s, &mut buf).unwrap();
220
    ///
221
    /// // shorten our vec down to just what was written
222
    /// buf.truncate(bytes_written);
223
    ///
224
    /// assert_eq!(s, general_purpose::STANDARD.decode(&buf).unwrap().as_slice());
225
    /// ```
226
    #[inline]
227
0
    fn encode_slice<T: AsRef<[u8]>>(
228
0
        &self,
229
0
        input: T,
230
0
        output_buf: &mut [u8],
231
0
    ) -> Result<usize, EncodeSliceError> {
232
0
        fn inner<E>(
233
0
            engine: &E,
234
0
            input_bytes: &[u8],
235
0
            output_buf: &mut [u8],
236
0
        ) -> Result<usize, EncodeSliceError>
237
0
        where
238
0
            E: Engine + ?Sized,
239
        {
240
0
            let encoded_size = encoded_len(input_bytes.len(), engine.config().encode_padding())
241
0
                .expect("usize overflow when calculating buffer size");
242
243
0
            if output_buf.len() < encoded_size {
244
0
                return Err(EncodeSliceError::OutputSliceTooSmall);
245
0
            }
246
247
0
            let b64_output = &mut output_buf[0..encoded_size];
248
249
0
            encode_with_padding(input_bytes, b64_output, engine, encoded_size);
250
251
0
            Ok(encoded_size)
252
0
        }
253
254
0
        inner(self, input.as_ref(), output_buf)
255
0
    }
256
257
    /// Decode the input into a new `Vec`.
258
    ///
259
    /// # Example
260
    ///
261
    /// ```rust
262
    /// use base64::{Engine as _, alphabet, engine::{self, general_purpose}};
263
    ///
264
    /// let bytes = general_purpose::STANDARD
265
    ///     .decode("aGVsbG8gd29ybGR+Cg==").unwrap();
266
    /// println!("{:?}", bytes);
267
    ///
268
    /// // custom engine setup
269
    /// let bytes_url = engine::GeneralPurpose::new(
270
    ///              &alphabet::URL_SAFE,
271
    ///              general_purpose::NO_PAD)
272
    ///     .decode("aGVsbG8gaW50ZXJuZXR-Cg").unwrap();
273
    /// println!("{:?}", bytes_url);
274
    /// ```
275
    #[cfg(any(feature = "alloc", test))]
276
    #[inline]
277
0
    fn decode<T: AsRef<[u8]>>(&self, input: T) -> Result<Vec<u8>, DecodeError> {
278
0
        fn inner<E>(engine: &E, input_bytes: &[u8]) -> Result<Vec<u8>, DecodeError>
279
0
        where
280
0
            E: Engine + ?Sized,
281
        {
282
0
            let estimate = engine.internal_decoded_len_estimate(input_bytes.len());
283
0
            let mut buffer = vec![0; estimate.decoded_len_estimate()];
284
285
0
            let bytes_written = engine
286
0
                .internal_decode(input_bytes, &mut buffer, estimate)
287
0
                .map_err(|e| match e {
288
0
                    DecodeSliceError::DecodeError(e) => e,
289
                    DecodeSliceError::OutputSliceTooSmall => {
290
0
                        unreachable!("Vec is sized conservatively")
291
                    }
292
0
                })?
293
                .decoded_len;
294
295
0
            buffer.truncate(bytes_written);
296
297
0
            Ok(buffer)
298
0
        }
299
300
0
        inner(self, input.as_ref())
301
0
    }
302
303
    /// Decode the `input` into the supplied `buffer`.
304
    ///
305
    /// Writes into the supplied `Vec`, which may allocate if its internal buffer isn't big enough.
306
    /// Returns a `Result` containing an empty tuple, aka `()`.
307
    ///
308
    /// # Example
309
    ///
310
    /// ```rust
311
    /// use base64::{Engine as _, alphabet, engine::{self, general_purpose}};
312
    /// const CUSTOM_ENGINE: engine::GeneralPurpose =
313
    ///     engine::GeneralPurpose::new(&alphabet::URL_SAFE, general_purpose::PAD);
314
    ///
315
    /// fn main() {
316
    ///     use base64::Engine;
317
    ///     let mut buffer = Vec::<u8>::new();
318
    ///     // with the default engine
319
    ///     general_purpose::STANDARD
320
    ///         .decode_vec("aGVsbG8gd29ybGR+Cg==", &mut buffer,).unwrap();
321
    ///     println!("{:?}", buffer);
322
    ///
323
    ///     buffer.clear();
324
    ///
325
    ///     // with a custom engine
326
    ///     CUSTOM_ENGINE.decode_vec(
327
    ///         "aGVsbG8gaW50ZXJuZXR-Cg==",
328
    ///         &mut buffer,
329
    ///     ).unwrap();
330
    ///     println!("{:?}", buffer);
331
    /// }
332
    /// ```
333
    #[cfg(any(feature = "alloc", test))]
334
    #[inline]
335
0
    fn decode_vec<T: AsRef<[u8]>>(
336
0
        &self,
337
0
        input: T,
338
0
        buffer: &mut Vec<u8>,
339
0
    ) -> Result<(), DecodeError> {
340
0
        fn inner<E>(engine: &E, input_bytes: &[u8], buffer: &mut Vec<u8>) -> Result<(), DecodeError>
341
0
        where
342
0
            E: Engine + ?Sized,
343
        {
344
0
            let starting_output_len = buffer.len();
345
0
            let estimate = engine.internal_decoded_len_estimate(input_bytes.len());
346
347
0
            let total_len_estimate = estimate
348
0
                .decoded_len_estimate()
349
0
                .checked_add(starting_output_len)
350
0
                .expect("Overflow when calculating output buffer length");
351
352
0
            buffer.resize(total_len_estimate, 0);
353
354
0
            let buffer_slice = &mut buffer.as_mut_slice()[starting_output_len..];
355
356
0
            let bytes_written = engine
357
0
                .internal_decode(input_bytes, buffer_slice, estimate)
358
0
                .map_err(|e| match e {
359
0
                    DecodeSliceError::DecodeError(e) => e,
360
                    DecodeSliceError::OutputSliceTooSmall => {
361
0
                        unreachable!("Vec is sized conservatively")
362
                    }
363
0
                })?
364
                .decoded_len;
365
366
0
            buffer.truncate(starting_output_len + bytes_written);
367
368
0
            Ok(())
369
0
        }
370
371
0
        inner(self, input.as_ref(), buffer)
372
0
    }
373
374
    /// Decode the input into the provided output slice.
375
    ///
376
    /// Returns the number of bytes written to the slice, or an error if `output` is smaller than
377
    /// the estimated decoded length.
378
    ///
379
    /// This will not write any bytes past exactly what is decoded (no stray garbage bytes at the end).
380
    ///
381
    /// See [`crate::decoded_len_estimate`] for calculating buffer sizes.
382
    ///
383
    /// See [`Engine::decode_slice_unchecked`] for a version that panics instead of returning an error
384
    /// if the output buffer is too small.
385
    #[inline]
386
0
    fn decode_slice<T: AsRef<[u8]>>(
387
0
        &self,
388
0
        input: T,
389
0
        output: &mut [u8],
390
0
    ) -> Result<usize, DecodeSliceError> {
391
0
        fn inner<E>(
392
0
            engine: &E,
393
0
            input_bytes: &[u8],
394
0
            output: &mut [u8],
395
0
        ) -> Result<usize, DecodeSliceError>
396
0
        where
397
0
            E: Engine + ?Sized,
398
        {
399
0
            engine
400
0
                .internal_decode(
401
0
                    input_bytes,
402
0
                    output,
403
0
                    engine.internal_decoded_len_estimate(input_bytes.len()),
404
                )
405
0
                .map(|dm| dm.decoded_len)
406
0
        }
407
408
0
        inner(self, input.as_ref(), output)
409
0
    }
410
411
    /// Decode the input into the provided output slice.
412
    ///
413
    /// Returns the number of bytes written to the slice.
414
    ///
415
    /// This will not write any bytes past exactly what is decoded (no stray garbage bytes at the end).
416
    ///
417
    /// See [`crate::decoded_len_estimate`] for calculating buffer sizes.
418
    ///
419
    /// See [`Engine::decode_slice`] for a version that returns an error instead of panicking if the output
420
    /// buffer is too small.
421
    ///
422
    /// # Panics
423
    ///
424
    /// Panics if the provided output buffer is too small for the decoded data.
425
    #[inline]
426
0
    fn decode_slice_unchecked<T: AsRef<[u8]>>(
427
0
        &self,
428
0
        input: T,
429
0
        output: &mut [u8],
430
0
    ) -> Result<usize, DecodeError> {
431
0
        fn inner<E>(engine: &E, input_bytes: &[u8], output: &mut [u8]) -> Result<usize, DecodeError>
432
0
        where
433
0
            E: Engine + ?Sized,
434
        {
435
0
            engine
436
0
                .internal_decode(
437
0
                    input_bytes,
438
0
                    output,
439
0
                    engine.internal_decoded_len_estimate(input_bytes.len()),
440
                )
441
0
                .map(|dm| dm.decoded_len)
442
0
                .map_err(|e| match e {
443
0
                    DecodeSliceError::DecodeError(e) => e,
444
                    DecodeSliceError::OutputSliceTooSmall => {
445
0
                        panic!("Output slice is too small")
446
                    }
447
0
                })
448
0
        }
449
450
0
        inner(self, input.as_ref(), output)
451
0
    }
452
453
    /// Returns the symbol used for encode padding.
454
    ///
455
    /// Typically this is `'='`, but weird alphabets may use other values.
456
    fn padding(&self) -> Symbol;
457
}
458
459
/// The minimal level of configuration that engines must support.
460
pub trait Config {
461
    /// Returns `true` if padding should be added after the encoded output.
462
    ///
463
    /// Padding is added outside the engine's `encode()` since the engine may be used
464
    /// to encode only a chunk of the overall output, so it can't always know when
465
    /// the output is "done" and would therefore need padding (if configured).
466
    // It could be provided as a separate parameter when encoding, but that feels like
467
    // leaking an implementation detail to the user, and it's hopefully more convenient
468
    // to have to only pass one thing (the engine) to any part of the API.
469
    fn encode_padding(&self) -> bool;
470
}
471
472
/// The decode estimate used by an engine implementation. Users do not need to interact with this;
473
/// it is only for engine implementors.
474
///
475
/// Implementors may store relevant data here when constructing this to avoid having to calculate
476
/// them again during actual decoding.
477
pub trait DecodeEstimate {
478
    /// Returns a conservative (err on the side of too big) estimate of the decoded length to use
479
    /// for pre-allocating buffers, etc.
480
    ///
481
    /// The estimate must be no larger than the next largest complete triple of decoded bytes.
482
    /// That is, the final quad of symbols to decode may be assumed to be complete with no padding.
483
    fn decoded_len_estimate(&self) -> usize;
484
}
485
486
/// Controls how pad bytes are handled when decoding.
487
///
488
/// Each [Engine] must support at least the behavior indicated by
489
/// [`DecodePaddingMode::RequireCanonical`], and may support other modes.
490
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
491
pub enum DecodePaddingMode {
492
    /// Canonical padding is allowed, but any fewer padding bytes than that is also allowed.
493
    Indifferent,
494
    /// Padding must be canonical (0, 1, or 2 `=` as needed to produce a 4 byte suffix).
495
    RequireCanonical,
496
    /// Padding must be absent -- for when you want predictable padding, without any wasted bytes.
497
    RequireNone,
498
}
499
500
/// Metadata about the result of a decode operation
501
#[derive(PartialEq, Eq, Debug)]
502
pub struct DecodeMetadata {
503
    /// Number of decoded bytes output
504
    pub(crate) decoded_len: usize,
505
    /// Offset of the first padding byte in the input, if any
506
    pub(crate) padding_offset: Option<usize>,
507
}
508
509
impl DecodeMetadata {
510
0
    pub(crate) fn new(decoded_bytes: usize, padding_index: Option<usize>) -> Self {
511
0
        Self {
512
0
            decoded_len: decoded_bytes,
513
0
            padding_offset: padding_index,
514
0
        }
515
0
    }
516
}