base64/
lib.rs

1//! # Configs
2//!
3//! There isn't just one type of Base64; that would be too simple. You need to choose a character
4//! set (standard, URL-safe, etc) and padding suffix (yes/no).
5//! The `Config` struct encapsulates this info. There are some common configs included: `STANDARD`,
6//! `URL_SAFE`, etc. You can also make your own `Config` if needed.
7//!
8//! The functions that don't have `config` in the name (e.g. `encode()` and `decode()`) use the
9//! `STANDARD` config .
10//!
11//! The functions that write to a slice (the ones that end in `_slice`) are generally the fastest
12//! because they don't need to resize anything. If it fits in your workflow and you care about
13//! performance, keep using the same buffer (growing as need be) and use the `_slice` methods for
14//! the best performance.
15//!
16//! # Encoding
17//!
18//! Several different encoding functions are available to you depending on your desire for
19//! convenience vs performance.
20//!
21//! | Function                | Output                       | Allocates                      |
22//! | ----------------------- | ---------------------------- | ------------------------------ |
23//! | `encode`                | Returns a new `String`       | Always                         |
24//! | `encode_config`         | Returns a new `String`       | Always                         |
25//! | `encode_config_buf`     | Appends to provided `String` | Only if `String` needs to grow |
26//! | `encode_config_slice`   | Writes to provided `&[u8]`   | Never                          |
27//!
28//! All of the encoding functions that take a `Config` will pad as per the config.
29//!
30//! # Decoding
31//!
32//! Just as for encoding, there are different decoding functions available.
33//!
34//! | Function                | Output                        | Allocates                      |
35//! | ----------------------- | ----------------------------- | ------------------------------ |
36//! | `decode`                | Returns a new `Vec<u8>`       | Always                         |
37//! | `decode_config`         | Returns a new `Vec<u8>`       | Always                         |
38//! | `decode_config_buf`     | Appends to provided `Vec<u8>` | Only if `Vec` needs to grow    |
39//! | `decode_config_slice`   | Writes to provided `&[u8]`    | Never                          |
40//!
41//! Unlike encoding, where all possible input is valid, decoding can fail (see `DecodeError`).
42//!
43//! Input can be invalid because it has invalid characters or invalid padding. (No padding at all is
44//! valid, but excess padding is not.) Whitespace in the input is invalid.
45//!
46//! # `Read` and `Write`
47//!
48//! To map a `Read` of b64 bytes to the decoded bytes, wrap a reader (file, network socket, etc)
49//! with `base64::read::DecoderReader`. To write raw bytes and have them b64 encoded on the fly,
50//! wrap a writer with `base64::write::EncoderWriter`. There is some performance overhead (15% or
51//! so) because of the necessary buffer shuffling -- still fast enough that almost nobody cares.
52//! Also, these implementations do not heap allocate.
53//!
54//! # Panics
55//!
56//! If length calculations result in overflowing `usize`, a panic will result.
57//!
58//! The `_slice` flavors of encode or decode will panic if the provided output slice is too small,
59
60#![cfg_attr(feature = "cargo-clippy", allow(clippy::cast_lossless))]
61#![deny(
62    missing_docs,
63    trivial_casts,
64    trivial_numeric_casts,
65    unused_extern_crates,
66    unused_import_braces,
67    unused_results,
68    variant_size_differences,
69    warnings
70)]
71#![forbid(unsafe_code)]
72#![cfg_attr(not(any(feature = "std", test)), no_std)]
73
74#[cfg(all(feature = "alloc", not(any(feature = "std", test))))]
75extern crate alloc;
76#[cfg(any(feature = "std", test))]
77extern crate std as alloc;
78
79mod chunked_encoder;
80pub mod display;
81#[cfg(any(feature = "std", test))]
82pub mod read;
83mod tables;
84#[cfg(any(feature = "std", test))]
85pub mod write;
86
87mod encode;
88pub use crate::encode::encode_config_slice;
89#[cfg(any(feature = "alloc", feature = "std", test))]
90pub use crate::encode::{encode, encode_config, encode_config_buf};
91
92mod decode;
93#[cfg(any(feature = "alloc", feature = "std", test))]
94pub use crate::decode::{decode, decode_config, decode_config_buf};
95pub use crate::decode::{decode_config_slice, DecodeError};
96
97#[cfg(test)]
98mod tests;
99
100/// Available encoding character sets
101#[derive(Clone, Copy, Debug)]
102pub enum CharacterSet {
103    /// The standard character set (uses `+` and `/`).
104    ///
105    /// See [RFC 3548](https://tools.ietf.org/html/rfc3548#section-3).
106    Standard,
107    /// The URL safe character set (uses `-` and `_`).
108    ///
109    /// See [RFC 3548](https://tools.ietf.org/html/rfc3548#section-4).
110    UrlSafe,
111    /// The `crypt(3)` character set (uses `./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`).
112    ///
113    /// Not standardized, but folk wisdom on the net asserts that this alphabet is what crypt uses.
114    Crypt,
115    /// The bcrypt character set (uses `./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`).
116    Bcrypt,
117    /// The character set used in IMAP-modified UTF-7 (uses `+` and `,`).
118    ///
119    /// See [RFC 3501](https://tools.ietf.org/html/rfc3501#section-5.1.3)
120    ImapMutf7,
121    /// The character set used in BinHex 4.0 files.
122    ///
123    /// See [BinHex 4.0 Definition](http://files.stairways.com/other/binhex-40-specs-info.txt)
124    BinHex,
125}
126
127impl CharacterSet {
128    fn encode_table(self) -> &'static [u8; 64] {
129        match self {
130            CharacterSet::Standard => tables::STANDARD_ENCODE,
131            CharacterSet::UrlSafe => tables::URL_SAFE_ENCODE,
132            CharacterSet::Crypt => tables::CRYPT_ENCODE,
133            CharacterSet::Bcrypt => tables::BCRYPT_ENCODE,
134            CharacterSet::ImapMutf7 => tables::IMAP_MUTF7_ENCODE,
135            CharacterSet::BinHex => tables::BINHEX_ENCODE,
136        }
137    }
138
139    fn decode_table(self) -> &'static [u8; 256] {
140        match self {
141            CharacterSet::Standard => tables::STANDARD_DECODE,
142            CharacterSet::UrlSafe => tables::URL_SAFE_DECODE,
143            CharacterSet::Crypt => tables::CRYPT_DECODE,
144            CharacterSet::Bcrypt => tables::BCRYPT_DECODE,
145            CharacterSet::ImapMutf7 => tables::IMAP_MUTF7_DECODE,
146            CharacterSet::BinHex => tables::BINHEX_DECODE,
147        }
148    }
149}
150
151/// Contains configuration parameters for base64 encoding
152#[derive(Clone, Copy, Debug)]
153pub struct Config {
154    /// Character set to use
155    char_set: CharacterSet,
156    /// True to pad output with `=` characters
157    pad: bool,
158    /// True to ignore excess nonzero bits in the last few symbols, otherwise an error is returned.
159    decode_allow_trailing_bits: bool,
160}
161
162impl Config {
163    /// Create a new `Config`.
164    pub const fn new(char_set: CharacterSet, pad: bool) -> Config {
165        Config {
166            char_set,
167            pad,
168            decode_allow_trailing_bits: false,
169        }
170    }
171
172    /// Sets whether to pad output with `=` characters.
173    pub const fn pad(self, pad: bool) -> Config {
174        Config { pad, ..self }
175    }
176
177    /// Sets whether to emit errors for nonzero trailing bits.
178    ///
179    /// This is useful when implementing
180    /// [forgiving-base64 decode](https://infra.spec.whatwg.org/#forgiving-base64-decode).
181    pub const fn decode_allow_trailing_bits(self, allow: bool) -> Config {
182        Config {
183            decode_allow_trailing_bits: allow,
184            ..self
185        }
186    }
187}
188
189/// Standard character set with padding.
190pub const STANDARD: Config = Config {
191    char_set: CharacterSet::Standard,
192    pad: true,
193    decode_allow_trailing_bits: false,
194};
195
196/// Standard character set without padding.
197pub const STANDARD_NO_PAD: Config = Config {
198    char_set: CharacterSet::Standard,
199    pad: false,
200    decode_allow_trailing_bits: false,
201};
202
203/// URL-safe character set with padding
204pub const URL_SAFE: Config = Config {
205    char_set: CharacterSet::UrlSafe,
206    pad: true,
207    decode_allow_trailing_bits: false,
208};
209
210/// URL-safe character set without padding
211pub const URL_SAFE_NO_PAD: Config = Config {
212    char_set: CharacterSet::UrlSafe,
213    pad: false,
214    decode_allow_trailing_bits: false,
215};
216
217/// As per `crypt(3)` requirements
218pub const CRYPT: Config = Config {
219    char_set: CharacterSet::Crypt,
220    pad: false,
221    decode_allow_trailing_bits: false,
222};
223
224/// Bcrypt character set
225pub const BCRYPT: Config = Config {
226    char_set: CharacterSet::Bcrypt,
227    pad: false,
228    decode_allow_trailing_bits: false,
229};
230
231/// IMAP modified UTF-7 requirements
232pub const IMAP_MUTF7: Config = Config {
233    char_set: CharacterSet::ImapMutf7,
234    pad: false,
235    decode_allow_trailing_bits: false,
236};
237
238/// BinHex character set
239pub const BINHEX: Config = Config {
240    char_set: CharacterSet::BinHex,
241    pad: false,
242    decode_allow_trailing_bits: false,
243};
244
245const PAD_BYTE: u8 = b'=';