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 )]
233 #![forbid(unsafe_code)]
234 // Allow globally until https://github.com/rust-lang/rust-clippy/issues/8768 is resolved.
235 // The desired state is to allow it only for the rstest_reuse import.
236 #![allow(clippy::single_component_path_imports)]
237 #![cfg_attr(not(any(feature = "std", test)), no_std)]
238 
239 #[cfg(any(feature = "alloc", test))]
240 extern crate alloc;
241 
242 // has to be included at top level because of the way rstest_reuse defines its macros
243 #[cfg(test)]
244 use rstest_reuse;
245 
246 mod chunked_encoder;
247 pub mod display;
248 #[cfg(any(feature = "std", test))]
249 pub mod read;
250 #[cfg(any(feature = "std", test))]
251 pub mod write;
252 
253 pub mod engine;
254 pub use engine::Engine;
255 
256 pub mod alphabet;
257 
258 mod encode;
259 #[allow(deprecated)]
260 #[cfg(any(feature = "alloc", test))]
261 pub use crate::encode::{encode, encode_engine, encode_engine_string};
262 #[allow(deprecated)]
263 pub use crate::encode::{encode_engine_slice, encoded_len, EncodeSliceError};
264 
265 mod decode;
266 #[allow(deprecated)]
267 #[cfg(any(feature = "alloc", test))]
268 pub use crate::decode::{decode, decode_engine, decode_engine_vec};
269 #[allow(deprecated)]
270 pub use crate::decode::{decode_engine_slice, decoded_len_estimate, DecodeError, DecodeSliceError};
271 
272 pub mod prelude;
273 
274 #[cfg(test)]
275 mod tests;
276 
277 const PAD_BYTE: u8 = b'=';
278