simple_base64/lib.rs
1//! # Getting started
2//!
3//! 1. Perhaps one of the preconfigured engines in [engine::general_purpose] will suit, e.g.
4//! [engine::general_purpose::STANDARD_NO_PAD].
5//! - These are re-exported in [prelude] with a `BASE64_` prefix for those who prefer to
6//! `use simple_base64::prelude::*` or equivalent, e.g. [prelude::BASE64_STANDARD_NO_PAD]
7//! 1. If not, choose which alphabet you want. Most usage will want [alphabet::STANDARD] or [alphabet::URL_SAFE].
8//! 1. Choose which [Engine] implementation you want. For the moment there is only one: [engine::GeneralPurpose].
9//! 1. Configure the engine appropriately using the engine's `Config` type.
10//! - This is where you'll select whether to add padding (when encoding) or expect it (when
11//! decoding). If given the choice, prefer no padding.
12//! 1. Build the engine using the selected alphabet and config.
13//!
14//! For more detail, see below.
15//!
16//! ## Alphabets
17//!
18//! An [alphabet::Alphabet] defines what ASCII symbols are used to encode to or decode from.
19//!
20//! Constants in [alphabet] like [alphabet::STANDARD] or [alphabet::URL_SAFE] provide commonly used
21//! alphabets, but you can also build your own custom [alphabet::Alphabet] if needed.
22//!
23//! ## Engines
24//!
25//! Once you have an `Alphabet`, you can pick which `Engine` you want. A few parts of the public
26//! API provide a default, but otherwise the user must provide an `Engine` to use.
27//!
28//! See [Engine] for more.
29//!
30//! ## Config
31//!
32//! In addition to an `Alphabet`, constructing an `Engine` also requires an [engine::Config]. Each
33//! `Engine` has a corresponding `Config` implementation since different `Engine`s may offer different
34//! levels of configurability.
35//!
36//! # Encoding
37//!
38//! Several different encoding methods on [Engine] are available to you depending on your desire for
39//! convenience vs performance.
40//!
41//! | Method | Output | Allocates |
42//! | ------------------------ | ---------------------------- | ------------------------------ |
43//! | [Engine::encode] | Returns a new `String` | Always |
44//! | [Engine::encode_string] | Appends to provided `String` | Only if `String` needs to grow |
45//! | [Engine::encode_slice] | Writes to provided `&[u8]` | Never - fastest |
46//!
47//! All of the encoding methods will pad as per the engine's config.
48//!
49//! # Decoding
50//!
51//! Just as for encoding, there are different decoding methods available.
52//!
53//! | Method | Output | Allocates |
54//! | ------------------------ | ----------------------------- | ------------------------------ |
55//! | [Engine::decode] | Returns a new `Vec<u8>` | Always |
56//! | [Engine::decode_vec] | Appends to provided `Vec<u8>` | Only if `Vec` needs to grow |
57//! | [Engine::decode_slice] | Writes to provided `&[u8]` | Never - fastest |
58//!
59//! Unlike encoding, where all possible input is valid, decoding can fail (see [DecodeError]).
60//!
61//! Input can be invalid because it has invalid characters or invalid padding. The nature of how
62//! padding is checked depends on the engine's config.
63//! Whitespace in the input is invalid, just like any other non-base64 byte.
64//!
65//! # `Read` and `Write`
66//!
67//! To decode a [std::io::Read] of b64 bytes, wrap a reader (file, network socket, etc) with
68//! [read::DecoderReader].
69//!
70//! To write raw bytes and have them b64 encoded on the fly, wrap a [std::io::Write] with
71//! [write::EncoderWriter].
72//!
73//! There is some performance overhead (15% or so) because of the necessary buffer shuffling --
74//! still fast enough that almost nobody cares. Also, these implementations do not heap allocate.
75//!
76//! # `Display`
77//!
78//! See [display] for how to transparently base64 data via a `Display` implementation.
79//!
80//! # Examples
81//!
82//! ## Using predefined engines
83//!
84#![cfg_attr(feature = "alloc", doc = "```")]
85#![cfg_attr(not(feature = "alloc"), doc = "```ignore")]
86//! use simple_base64::{Engine as _, engine::general_purpose};
87//!
88//! let orig = b"data";
89//! let encoded: String = general_purpose::STANDARD_NO_PAD.encode(orig);
90//! assert_eq!("ZGF0YQ", encoded);
91//! assert_eq!(orig.as_slice(), &general_purpose::STANDARD_NO_PAD.decode(encoded).unwrap());
92//!
93//! // or, URL-safe
94//! let encoded_url = general_purpose::URL_SAFE_NO_PAD.encode(orig);
95//! ```
96//!
97//! ## Custom alphabet, config, and engine
98//!
99#![cfg_attr(feature = "alloc", doc = "```")]
100#![cfg_attr(not(feature = "alloc"), doc = "```ignore")]
101//! use simple_base64::{engine, alphabet, Engine as _};
102//!
103//! // bizarro-world base64: +/ as the first symbols instead of the last
104//! let alphabet =
105//! alphabet::Alphabet::new("+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
106//! .unwrap();
107//!
108//! // a very weird config that encodes with padding but requires no padding when decoding...?
109//! let crazy_config = engine::GeneralPurposeConfig::new()
110//! .with_decode_allow_trailing_bits(true)
111//! .with_encode_padding(true)
112//! .with_decode_padding_mode(engine::DecodePaddingMode::RequireNone);
113//!
114//! let crazy_engine = engine::GeneralPurpose::new(&alphabet, crazy_config);
115//!
116//! let encoded = crazy_engine.encode(b"abc 123");
117//!
118//! ```
119//!
120//! # Panics
121//!
122//! If length calculations result in overflowing `usize`, a panic will result.
123
124#![cfg_attr(feature = "cargo-clippy", allow(clippy::cast_lossless))]
125#![deny(
126 missing_docs,
127 trivial_casts,
128 trivial_numeric_casts,
129 unused_extern_crates,
130 unused_import_braces,
131 unused_results,
132 variant_size_differences,
133 warnings
134)]
135#![forbid(unsafe_code)]
136// Allow globally until https://github.com/rust-lang/rust-clippy/issues/8768 is resolved.
137// The desired state is to allow it only for the rstest_reuse import.
138#![allow(clippy::single_component_path_imports)]
139#![cfg_attr(not(any(feature = "std", test)), no_std)]
140
141#[cfg(any(feature = "alloc", test))]
142extern crate alloc;
143
144// has to be included at top level because of the way rstest_reuse defines its macros
145#[cfg(test)]
146use rstest_reuse;
147
148mod chunked_encoder;
149pub mod display;
150#[cfg(any(feature = "std", test))]
151pub mod read;
152#[cfg(any(feature = "std", test))]
153pub mod write;
154
155pub mod engine;
156pub use engine::Engine;
157
158pub mod alphabet;
159
160mod encode;
161#[cfg(any(feature = "alloc", test))]
162pub use crate::encode::{encode, encode_engine, encode_engine_string};
163pub use crate::encode::{encode_engine_slice, encoded_len, EncodeSliceError};
164
165mod decode;
166#[cfg(any(feature = "alloc", test))]
167pub use crate::decode::{decode, decode_engine, decode_engine_vec};
168pub use crate::decode::{decode_engine_slice, decoded_len_estimate, DecodeError, DecodeSliceError};
169
170pub mod prelude;
171
172#[cfg(test)]
173mod tests;
174
175const PAD_BYTE: u8 = b'=';