#![doc=vsimd::shared_docs!()]
#![cfg_attr(not(any(feature = "std", test)), no_std)]
#![cfg_attr(feature = "unstable", feature(arm_target_feature))]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(test, deny(warnings))]
#![deny(
missing_debug_implementations,
missing_docs,
clippy::all,
clippy::pedantic,
clippy::cargo,
clippy::missing_inline_in_public_items
)]
#![warn(clippy::todo)]
#![allow(
clippy::inline_always,
clippy::wildcard_imports,
clippy::module_name_repetitions,
clippy::cast_sign_loss,
clippy::cast_possible_truncation,
clippy::cast_lossless,
clippy::cast_possible_wrap,
clippy::items_after_statements,
clippy::match_same_arms,
clippy::verbose_bit_mask
)]
#[cfg(feature = "alloc")]
extern crate alloc;
#[macro_use]
mod error;
pub use self::error::Error;
mod alsw;
mod ascii;
mod check;
mod decode;
mod encode;
mod multiversion;
#[cfg(feature = "alloc")]
mod heap;
mod forgiving;
pub use self::forgiving::*;
pub use outref::{AsOut, Out};
use crate::decode::decoded_length;
use crate::encode::encoded_length_unchecked;
use vsimd::tools::{slice_mut, slice_parts};
#[cfg(feature = "alloc")]
use alloc::{string::String, vec::Vec};
const STANDARD_CHARSET: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const URL_SAFE_CHARSET: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
#[derive(Debug)]
pub struct Base64 {
config: Config,
}
#[derive(Debug, Clone, Copy)]
enum Kind {
Standard,
UrlSafe,
}
#[derive(Debug, Clone, Copy)]
struct Config {
kind: Kind,
extra: Extra,
}
#[derive(Debug, Clone, Copy)]
enum Extra {
Pad,
NoPad,
Forgiving,
}
impl Extra {
#[inline(always)]
#[must_use]
const fn padding(self) -> bool {
match self {
Extra::Pad => true,
Extra::NoPad => false,
Extra::Forgiving => true,
}
}
#[inline(always)]
#[must_use]
const fn forgiving(self) -> bool {
match self {
Extra::Pad => false,
Extra::NoPad => false,
Extra::Forgiving => true,
}
}
}
pub const STANDARD: Base64 = Base64 {
config: Config {
kind: Kind::Standard,
extra: Extra::Pad,
},
};
pub const URL_SAFE: Base64 = Base64 {
config: Config {
kind: Kind::UrlSafe,
extra: Extra::Pad,
},
};
pub const STANDARD_NO_PAD: Base64 = Base64 {
config: Config {
kind: Kind::Standard,
extra: Extra::NoPad,
},
};
pub const URL_SAFE_NO_PAD: Base64 = Base64 {
config: Config {
kind: Kind::UrlSafe,
extra: Extra::NoPad,
},
};
const STANDARD_FORGIVING: Base64 = Base64 {
config: Config {
kind: Kind::Standard,
extra: Extra::Forgiving,
},
};
impl Base64 {
#[inline]
#[must_use]
pub const fn charset(&self) -> &[u8; 64] {
match self.config.kind {
Kind::Standard => STANDARD_CHARSET,
Kind::UrlSafe => URL_SAFE_CHARSET,
}
}
#[inline]
#[must_use]
pub const fn encoded_length(&self, n: usize) -> usize {
assert!(n <= usize::MAX / 2);
encoded_length_unchecked(n, self.config)
}
#[inline]
#[must_use]
pub const fn estimated_decoded_length(&self, n: usize) -> usize {
if n % 4 == 0 {
n / 4 * 3
} else {
(n / 4 + 1) * 3
}
}
#[inline]
pub fn decoded_length(&self, data: &[u8]) -> Result<usize, Error> {
let (_, m) = decoded_length(data, self.config)?;
Ok(m)
}
#[inline]
pub fn check(&self, data: &[u8]) -> Result<(), Error> {
let (n, _) = decoded_length(data, self.config)?;
unsafe { crate::multiversion::check::auto(data.as_ptr(), n, self.config) }
}
#[inline]
#[must_use]
pub fn encode<'d>(&self, src: &[u8], mut dst: Out<'d, [u8]>) -> &'d mut [u8] {
unsafe {
let m = encoded_length_unchecked(src.len(), self.config);
assert!(dst.len() >= m);
let (src, len) = slice_parts(src);
let dst = dst.as_mut_ptr();
self::multiversion::encode::auto(src, len, dst, self.config);
slice_mut(dst, m)
}
}
#[inline]
#[must_use]
pub fn encode_as_str<'d>(&self, src: &[u8], dst: Out<'d, [u8]>) -> &'d mut str {
let ans = self.encode(src, dst);
unsafe { core::str::from_utf8_unchecked_mut(ans) }
}
#[inline]
pub fn decode<'d>(&self, src: &[u8], mut dst: Out<'d, [u8]>) -> Result<&'d mut [u8], Error> {
unsafe {
let (n, m) = decoded_length(src, self.config)?;
assert!(dst.len() >= m);
let src = src.as_ptr();
let dst = dst.as_mut_ptr();
self::multiversion::decode::auto(src, dst, n, self.config)?;
Ok(slice_mut(dst, m))
}
}
#[inline]
pub fn decode_inplace<'d>(&self, data: &'d mut [u8]) -> Result<&'d mut [u8], Error> {
unsafe {
let (n, m) = decoded_length(data, self.config)?;
let dst: *mut u8 = data.as_mut_ptr();
let src: *const u8 = dst;
self::multiversion::decode::auto(src, dst, n, self.config)?;
Ok(slice_mut(dst, m))
}
}
#[inline]
#[must_use]
pub fn encode_type<T: FromBase64Encode>(&self, data: impl AsRef<[u8]>) -> T {
T::from_base64_encode(self, data.as_ref())
}
#[inline]
pub fn decode_type<T: FromBase64Decode>(&self, data: impl AsRef<[u8]>) -> Result<T, Error> {
T::from_base64_decode(self, data.as_ref())
}
#[inline]
pub fn encode_append<T: AppendBase64Encode>(&self, src: impl AsRef<[u8]>, dst: &mut T) {
T::append_base64_encode(self, src.as_ref(), dst);
}
#[inline]
pub fn decode_append<T: AppendBase64Decode>(&self, src: impl AsRef<[u8]>, dst: &mut T) -> Result<(), Error> {
T::append_base64_decode(self, src.as_ref(), dst)
}
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
#[cfg(feature = "alloc")]
#[inline]
#[must_use]
pub fn encode_to_string(&self, data: impl AsRef<[u8]>) -> String {
self.encode_type(data)
}
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
#[cfg(feature = "alloc")]
#[inline]
pub fn decode_to_vec(&self, data: impl AsRef<[u8]>) -> Result<Vec<u8>, Error> {
self.decode_type(data)
}
}
pub trait FromBase64Encode: Sized {
fn from_base64_encode(base64: &Base64, data: &[u8]) -> Self;
}
pub trait FromBase64Decode: Sized {
fn from_base64_decode(base64: &Base64, data: &[u8]) -> Result<Self, Error>;
}
pub trait AppendBase64Encode: FromBase64Encode {
fn append_base64_encode(base64: &Base64, src: &[u8], dst: &mut Self);
}
pub trait AppendBase64Decode: FromBase64Decode {
fn append_base64_decode(base64: &Base64, src: &[u8], dst: &mut Self) -> Result<(), Error>;
}