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