1 | //! Correct, fast, and configurable [base64][] decoding and encoding. Base64 |
2 | //! transports binary data efficiently in contexts where only plain text is |
3 | //! allowed. |
4 | //! |
5 | //! [base64]: https://developer.mozilla.org/en-US/docs/Glossary/Base64 |
6 | //! |
7 | //! # Usage |
8 | //! |
9 | //! Use an [`Engine`] to decode or encode base64, configured with the base64 |
10 | //! alphabet and padding behavior best suited to your application. |
11 | //! |
12 | //! ## Engine setup |
13 | //! |
14 | //! There is more than one way to encode a stream of bytes as “base64”. |
15 | //! Different applications use different encoding |
16 | //! [alphabets][alphabet::Alphabet] and |
17 | //! [padding behaviors][engine::general_purpose::GeneralPurposeConfig]. |
18 | //! |
19 | //! ### Encoding alphabet |
20 | //! |
21 | //! Almost all base64 [alphabets][alphabet::Alphabet] use `A-Z`, `a-z`, and |
22 | //! `0-9`, which gives nearly 64 characters (26 + 26 + 10 = 62), but they differ |
23 | //! in their choice of their final 2. |
24 | //! |
25 | //! Most applications use the [standard][alphabet::STANDARD] alphabet specified |
26 | //! in [RFC 4648][rfc-alphabet]. If that’s all you need, you can get started |
27 | //! quickly by using the pre-configured |
28 | //! [`STANDARD`][engine::general_purpose::STANDARD] engine, which is also available |
29 | //! in the [`prelude`] module as shown here, if you prefer a minimal `use` |
30 | //! footprint. |
31 | //! |
32 | #![cfg_attr (feature = "alloc" , doc = "```" )] |
33 | #![cfg_attr (not(feature = "alloc" ), doc = "```ignore" )] |
34 | //! use base64::prelude::*; |
35 | //! |
36 | //! # fn main() -> Result<(), base64::DecodeError> { |
37 | //! assert_eq!(BASE64_STANDARD.decode(b"+uwgVQA=" )?, b" \xFA\xEC\x20\x55\0" ); |
38 | //! assert_eq!(BASE64_STANDARD.encode(b" \xFF\xEC\x20\x55\0" ), "/+wgVQA=" ); |
39 | //! # Ok(()) |
40 | //! # } |
41 | //! ``` |
42 | //! |
43 | //! [rfc-alphabet]: https://datatracker.ietf.org/doc/html/rfc4648#section-4 |
44 | //! |
45 | //! Other common alphabets are available in the [`alphabet`] module. |
46 | //! |
47 | //! #### URL-safe alphabet |
48 | //! |
49 | //! The standard alphabet uses `+` and `/` as its two non-alphanumeric tokens, |
50 | //! which cannot be safely used in URL’s without encoding them as `%2B` and |
51 | //! `%2F`. |
52 | //! |
53 | //! To avoid that, some applications use a [“URL-safe” alphabet][alphabet::URL_SAFE], |
54 | //! which uses `-` and `_` instead. To use that alternative alphabet, use the |
55 | //! [`URL_SAFE`][engine::general_purpose::URL_SAFE] engine. This example doesn't |
56 | //! use [`prelude`] to show what a more explicit `use` would look like. |
57 | //! |
58 | #![cfg_attr (feature = "alloc" , doc = "```" )] |
59 | #![cfg_attr (not(feature = "alloc" ), doc = "```ignore" )] |
60 | //! use base64::{engine::general_purpose::URL_SAFE, Engine as _}; |
61 | //! |
62 | //! # fn main() -> Result<(), base64::DecodeError> { |
63 | //! assert_eq!(URL_SAFE.decode(b"-uwgVQA=" )?, b" \xFA\xEC\x20\x55\0" ); |
64 | //! assert_eq!(URL_SAFE.encode(b" \xFF\xEC\x20\x55\0" ), "_-wgVQA=" ); |
65 | //! # Ok(()) |
66 | //! # } |
67 | //! ``` |
68 | //! |
69 | //! ### Padding characters |
70 | //! |
71 | //! Each base64 character represents 6 bits (2⁶ = 64) of the original binary |
72 | //! data, and every 3 bytes of input binary data will encode to 4 base64 |
73 | //! characters (8 bits × 3 = 6 bits × 4 = 24 bits). |
74 | //! |
75 | //! When the input is not an even multiple of 3 bytes in length, [canonical][] |
76 | //! base64 encoders insert padding characters at the end, so that the output |
77 | //! length is always a multiple of 4: |
78 | //! |
79 | //! [canonical]: https://datatracker.ietf.org/doc/html/rfc4648#section-3.5 |
80 | //! |
81 | #![cfg_attr (feature = "alloc" , doc = "```" )] |
82 | #![cfg_attr (not(feature = "alloc" ), doc = "```ignore" )] |
83 | //! use base64::{engine::general_purpose::STANDARD, Engine as _}; |
84 | //! |
85 | //! assert_eq!(STANDARD.encode(b"" ), "" ); |
86 | //! assert_eq!(STANDARD.encode(b"f" ), "Zg==" ); |
87 | //! assert_eq!(STANDARD.encode(b"fo" ), "Zm8=" ); |
88 | //! assert_eq!(STANDARD.encode(b"foo" ), "Zm9v" ); |
89 | //! ``` |
90 | //! |
91 | //! Canonical encoding ensures that base64 encodings will be exactly the same, |
92 | //! byte-for-byte, regardless of input length. But the `=` padding characters |
93 | //! aren’t necessary for decoding, and they may be omitted by using a |
94 | //! [`NO_PAD`][engine::general_purpose::NO_PAD] configuration: |
95 | //! |
96 | #![cfg_attr (feature = "alloc" , doc = "```" )] |
97 | #![cfg_attr (not(feature = "alloc" ), doc = "```ignore" )] |
98 | //! use base64::{engine::general_purpose::STANDARD_NO_PAD, Engine as _}; |
99 | //! |
100 | //! assert_eq!(STANDARD_NO_PAD.encode(b"" ), "" ); |
101 | //! assert_eq!(STANDARD_NO_PAD.encode(b"f" ), "Zg" ); |
102 | //! assert_eq!(STANDARD_NO_PAD.encode(b"fo" ), "Zm8" ); |
103 | //! assert_eq!(STANDARD_NO_PAD.encode(b"foo" ), "Zm9v" ); |
104 | //! ``` |
105 | //! |
106 | //! The pre-configured `NO_PAD` engines will reject inputs containing padding |
107 | //! `=` characters. To encode without padding and still accept padding while |
108 | //! decoding, create an [engine][engine::general_purpose::GeneralPurpose] with |
109 | //! that [padding mode][engine::DecodePaddingMode]. |
110 | //! |
111 | #![cfg_attr (feature = "alloc" , doc = "```" )] |
112 | #![cfg_attr (not(feature = "alloc" ), doc = "```ignore" )] |
113 | //! # use base64::{engine::general_purpose::STANDARD_NO_PAD, Engine as _}; |
114 | //! assert_eq!(STANDARD_NO_PAD.decode(b"Zm8=" ), Err(base64::DecodeError::InvalidPadding)); |
115 | //! ``` |
116 | //! |
117 | //! ### Further customization |
118 | //! |
119 | //! Decoding and encoding behavior can be customized by creating an |
120 | //! [engine][engine::GeneralPurpose] with an [alphabet][alphabet::Alphabet] and |
121 | //! [padding configuration][engine::GeneralPurposeConfig]: |
122 | //! |
123 | #![cfg_attr (feature = "alloc" , doc = "```" )] |
124 | #![cfg_attr (not(feature = "alloc" ), doc = "```ignore" )] |
125 | //! use base64::{engine, alphabet, Engine as _}; |
126 | //! |
127 | //! // bizarro-world base64: +/ as the first symbols instead of the last |
128 | //! let alphabet = |
129 | //! alphabet::Alphabet::new("+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" ) |
130 | //! .unwrap(); |
131 | //! |
132 | //! // a very weird config that encodes with padding but requires no padding when decoding...? |
133 | //! let crazy_config = engine::GeneralPurposeConfig::new() |
134 | //! .with_decode_allow_trailing_bits(true) |
135 | //! .with_encode_padding(true) |
136 | //! .with_decode_padding_mode(engine::DecodePaddingMode::RequireNone); |
137 | //! |
138 | //! let crazy_engine = engine::GeneralPurpose::new(&alphabet, crazy_config); |
139 | //! |
140 | //! let encoded = crazy_engine.encode(b"abc 123" ); |
141 | //! |
142 | //! ``` |
143 | //! |
144 | //! ## Memory allocation |
145 | //! |
146 | //! The [decode][Engine::decode()] and [encode][Engine::encode()] engine methods |
147 | //! allocate memory for their results – `decode` returns a `Vec<u8>` and |
148 | //! `encode` returns a `String`. To instead decode or encode into a buffer that |
149 | //! you allocated, use one of the alternative methods: |
150 | //! |
151 | //! #### Decoding |
152 | //! |
153 | //! | Method | Output | Allocates memory | |
154 | //! | -------------------------- | ----------------------------- | ----------------------------- | |
155 | //! | [`Engine::decode`] | returns a new `Vec<u8>` | always | |
156 | //! | [`Engine::decode_vec`] | appends to provided `Vec<u8>` | if `Vec` lacks capacity | |
157 | //! | [`Engine::decode_slice`] | writes to provided `&[u8]` | never |
158 | //! |
159 | //! #### Encoding |
160 | //! |
161 | //! | Method | Output | Allocates memory | |
162 | //! | -------------------------- | ---------------------------- | ------------------------------ | |
163 | //! | [`Engine::encode`] | returns a new `String` | always | |
164 | //! | [`Engine::encode_string`] | appends to provided `String` | if `String` lacks capacity | |
165 | //! | [`Engine::encode_slice`] | writes to provided `&[u8]` | never | |
166 | //! |
167 | //! ## Input and output |
168 | //! |
169 | //! The `base64` crate can [decode][Engine::decode()] and |
170 | //! [encode][Engine::encode()] values in memory, or |
171 | //! [`DecoderReader`][read::DecoderReader] and |
172 | //! [`EncoderWriter`][write::EncoderWriter] provide streaming decoding and |
173 | //! encoding for any [readable][std::io::Read] or [writable][std::io::Write] |
174 | //! byte stream. |
175 | //! |
176 | //! #### Decoding |
177 | //! |
178 | #![cfg_attr (feature = "std" , doc = "```" )] |
179 | #![cfg_attr (not(feature = "std" ), doc = "```ignore" )] |
180 | //! # use std::io; |
181 | //! use base64::{engine::general_purpose::STANDARD, read::DecoderReader}; |
182 | //! |
183 | //! # fn main() -> Result<(), Box<dyn std::error::Error>> { |
184 | //! let mut input = io::stdin(); |
185 | //! let mut decoder = DecoderReader::new(&mut input, &STANDARD); |
186 | //! io::copy(&mut decoder, &mut io::stdout())?; |
187 | //! # Ok(()) |
188 | //! # } |
189 | //! ``` |
190 | //! |
191 | //! #### Encoding |
192 | //! |
193 | #![cfg_attr (feature = "std" , doc = "```" )] |
194 | #![cfg_attr (not(feature = "std" ), doc = "```ignore" )] |
195 | //! # use std::io; |
196 | //! use base64::{engine::general_purpose::STANDARD, write::EncoderWriter}; |
197 | //! |
198 | //! # fn main() -> Result<(), Box<dyn std::error::Error>> { |
199 | //! let mut output = io::stdout(); |
200 | //! let mut encoder = EncoderWriter::new(&mut output, &STANDARD); |
201 | //! io::copy(&mut io::stdin(), &mut encoder)?; |
202 | //! # Ok(()) |
203 | //! # } |
204 | //! ``` |
205 | //! |
206 | //! #### Display |
207 | //! |
208 | //! If you only need a base64 representation for implementing the |
209 | //! [`Display`][std::fmt::Display] trait, use |
210 | //! [`Base64Display`][display::Base64Display]: |
211 | //! |
212 | //! ``` |
213 | //! use base64::{display::Base64Display, engine::general_purpose::STANDARD}; |
214 | //! |
215 | //! let value = Base64Display::new(b" \0\x01\x02\x03" , &STANDARD); |
216 | //! assert_eq!("base64: AAECAw==" , format!("base64: {}" , value)); |
217 | //! ``` |
218 | //! |
219 | //! # Panics |
220 | //! |
221 | //! If length calculations result in overflowing `usize`, a panic will result. |
222 | |
223 | #![cfg_attr (feature = "cargo-clippy" , allow(clippy::cast_lossless))] |
224 | #![deny ( |
225 | missing_docs, |
226 | trivial_casts, |
227 | trivial_numeric_casts, |
228 | unused_extern_crates, |
229 | unused_import_braces, |
230 | unused_results, |
231 | variant_size_differences, |
232 | warnings |
233 | )] |
234 | #![forbid (unsafe_code)] |
235 | // Allow globally until https://github.com/rust-lang/rust-clippy/issues/8768 is resolved. |
236 | // The desired state is to allow it only for the rstest_reuse import. |
237 | #![allow (clippy::single_component_path_imports)] |
238 | #![cfg_attr (not(any(feature = "std" , test)), no_std)] |
239 | |
240 | #[cfg (any(feature = "alloc" , test))] |
241 | extern crate alloc; |
242 | |
243 | // has to be included at top level because of the way rstest_reuse defines its macros |
244 | #[cfg (test)] |
245 | use rstest_reuse; |
246 | |
247 | mod chunked_encoder; |
248 | pub mod display; |
249 | #[cfg (any(feature = "std" , test))] |
250 | pub mod read; |
251 | #[cfg (any(feature = "std" , test))] |
252 | pub mod write; |
253 | |
254 | pub mod engine; |
255 | pub use engine::Engine; |
256 | |
257 | pub mod alphabet; |
258 | |
259 | mod encode; |
260 | #[allow (deprecated)] |
261 | #[cfg (any(feature = "alloc" , test))] |
262 | pub use crate::encode::{encode, encode_engine, encode_engine_string}; |
263 | #[allow (deprecated)] |
264 | pub use crate::encode::{encode_engine_slice, encoded_len, EncodeSliceError}; |
265 | |
266 | mod decode; |
267 | #[allow (deprecated)] |
268 | #[cfg (any(feature = "alloc" , test))] |
269 | pub use crate::decode::{decode, decode_engine, decode_engine_vec}; |
270 | #[allow (deprecated)] |
271 | pub use crate::decode::{decode_engine_slice, decoded_len_estimate, DecodeError, DecodeSliceError}; |
272 | |
273 | pub mod prelude; |
274 | |
275 | #[cfg (test)] |
276 | mod tests; |
277 | |
278 | const PAD_BYTE: u8 = b'=' ; |
279 | |