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))))]
75 extern crate alloc;
76 #[cfg(any(feature = "std", test))]
77 extern crate std as alloc;
78 
79 mod chunked_encoder;
80 pub mod display;
81 #[cfg(any(feature = "std", test))]
82 pub mod read;
83 mod tables;
84 #[cfg(any(feature = "std", test))]
85 pub mod write;
86 
87 mod encode;
88 pub use crate::encode::encode_config_slice;
89 #[cfg(any(feature = "alloc", feature = "std", test))]
90 pub use crate::encode::{encode, encode_config, encode_config_buf};
91 
92 mod decode;
93 #[cfg(any(feature = "alloc", feature = "std", test))]
94 pub use crate::decode::{decode, decode_config, decode_config_buf};
95 pub use crate::decode::{decode_config_slice, DecodeError};
96 
97 #[cfg(test)]
98 mod tests;
99 
100 /// Available encoding character sets
101 #[derive(Clone, Copy, Debug)]
102 pub 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 
127 impl CharacterSet {
encode_table(self) -> &'static [u8; 64]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 
decode_table(self) -> &'static [u8; 256]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)]
153 pub 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 
162 impl Config {
163     /// Create a new `Config`.
new(char_set: CharacterSet, pad: bool) -> Config164     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.
pad(self, pad: bool) -> Config173     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).
decode_allow_trailing_bits(self, allow: bool) -> Config181     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.
190 pub 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.
197 pub 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
204 pub 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
211 pub 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
218 pub const CRYPT: Config = Config {
219     char_set: CharacterSet::Crypt,
220     pad: false,
221     decode_allow_trailing_bits: false,
222 };
223 
224 /// Bcrypt character set
225 pub 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
232 pub 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
239 pub const BINHEX: Config = Config {
240     char_set: CharacterSet::BinHex,
241     pad: false,
242     decode_allow_trailing_bits: false,
243 };
244 
245 const PAD_BYTE: u8 = b'=';
246