pub(crate) mod alphabet;
mod decode;
mod display;
mod encode;
mod fold;
mod kernel;
#[cfg(test)]
mod tests;
pub use crate::Fold;
pub use decode::{Decoder, FoldedValue};
pub use display::Display;
use alphabet::Tables;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Alphabet {
Standard,
UrlSafe,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Padding {
Required,
Omitted,
Optional,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LineEnding {
Lf,
CrLf,
}
impl LineEnding {
pub(crate) const fn as_bytes(self) -> &'static [u8] {
match self {
LineEnding::Lf => b"\n",
LineEnding::CrLf => b"\r\n",
}
}
pub(crate) const fn len(self) -> usize {
self.as_bytes().len()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct Wrap {
pub(crate) width: usize,
pub(crate) ending: LineEnding,
}
impl Wrap {
pub(crate) const fn line_in(self) -> usize {
self.width / 4 * 3
}
pub(crate) const fn line_out(self) -> usize {
self.width + self.ending.len()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Base64 {
alphabet: Alphabet,
padding: Padding,
lenient: bool,
any_alphabet: bool,
wrap: Option<Wrap>,
}
pub const STANDARD: Base64 = Base64::new(Alphabet::Standard);
pub const STANDARD_NO_PAD: Base64 = STANDARD.with_padding(Padding::Omitted);
pub const URL_SAFE: Base64 = Base64::new(Alphabet::UrlSafe);
pub const URL_SAFE_NO_PAD: Base64 = URL_SAFE.with_padding(Padding::Omitted);
pub const LENIENT: Base64 = STANDARD.lenient();
pub const MIME: Base64 = LENIENT.wrapped(76, LineEnding::CrLf);
impl Base64 {
pub const fn new(alphabet: Alphabet) -> Self {
Base64 {
alphabet,
padding: Padding::Required,
lenient: false,
any_alphabet: false,
wrap: None,
}
}
pub const fn with_padding(mut self, padding: Padding) -> Self {
self.padding = padding;
self
}
pub const fn lenient(mut self) -> Self {
self.lenient = true;
self
}
pub const fn any_alphabet(mut self) -> Self {
self.any_alphabet = true;
self
}
pub const fn wrapped(mut self, width: usize, ending: LineEnding) -> Self {
let width = if width < 4 { 4 } else { width & !3 };
self.wrap = Some(Wrap { width, ending });
self
}
pub const fn unwrapped(mut self) -> Self {
self.wrap = None;
self
}
pub const fn alphabet(&self) -> Alphabet {
self.alphabet
}
pub const fn padding(&self) -> Padding {
self.padding
}
pub const fn is_lenient(&self) -> bool {
self.lenient
}
#[inline(always)]
pub(crate) const fn tables(&self) -> &'static Tables {
match self.alphabet {
Alphabet::Standard => &alphabet::STANDARD,
Alphabet::UrlSafe => &alphabet::URL_SAFE,
}
}
#[inline(always)]
pub(crate) const fn decode_tables(&self) -> &'static Tables {
if self.any_alphabet {
&alphabet::ANY
} else {
self.tables()
}
}
#[inline(always)]
pub(crate) const fn pads(&self) -> bool {
!matches!(self.padding, Padding::Omitted)
}
pub const fn encoded_len(&self, input_len: usize) -> usize {
let symbols = if self.pads() {
input_len.div_ceil(3).saturating_mul(4)
} else {
(input_len / 3).saturating_mul(4)
+ match input_len % 3 {
0 => 0,
1 => 2,
_ => 3,
}
};
match self.wrap {
Some(wrap) => symbols.saturating_add(
symbols
.div_ceil(wrap.width)
.saturating_mul(wrap.ending.len()),
),
None => symbols,
}
}
pub const fn decoded_len_estimate(&self, input_len: usize) -> usize {
input_len.div_ceil(4).saturating_mul(3)
}
}