#![no_std]
#[cfg(feature = "alloc")]
extern crate alloc;
use core::{fmt::Debug, iter, ops::Deref, str};
#[macro_use]
mod macros;
pub mod alphabet;
pub mod base32;
pub mod base64;
pub mod base85;
pub mod generic;
pub mod hex;
pub mod padding;
mod chunked;
use self::alphabet::{Alphabet, DecodingTable, DynAlphabet, DynDecodingTable};
pub use self::{
base32::{
Base32HexLower, Base32HexLowerUnpadded, Base32HexUpper, Base32HexUpperUnpadded,
Base32Lower, Base32LowerUnpadded, Base32Upper, Base32UpperUnpadded
},
base64::{Base64, Base64Unpadded, Base64UrlPadded, Base64UrlUnpadded},
base85::Z85,
hex::{HexLower, HexUpper}
};
pub trait Encoder<const LEN: usize>: DynEncoder + Decoder<LEN> {
fn alphabet(&self) -> &Alphabet<LEN>;
#[inline]
fn encode_array<const LEN2: usize>(
&self,
src: &[u8]
) -> Result<ArrayString<LEN2>, EncodeIntoExactError> {
let mut bytes = [0; LEN2];
let _ = self.encode_into_exact(src, &mut bytes)?;
Ok(ArrayString { bytes })
}
}
pub trait DynEncoder: DynDecoder {
fn dyn_alphabet(&self) -> &DynAlphabet;
fn encoded_len(&self, len: usize) -> Option<usize>;
fn encode_into_exact<'a>(
&self,
src: &[u8],
dst: &'a mut [u8]
) -> Result<&'a str, EncodeIntoExactError>;
#[inline]
fn encode_into<'a>(&self, src: &[u8], dst: &'a mut [u8]) -> Result<&'a str, EncodeIntoError> {
let dst = dst
.get_mut(..self.encoded_len(src.len()).ok_or(EncodeIntoError::LengthOverflow)?)
.ok_or(EncodeIntoError::TooSmall)?;
Ok(self.encode_into_exact(src, dst).unwrap())
}
#[cfg(feature = "alloc")]
#[inline]
fn encode_string(&self, src: &[u8]) -> Option<alloc::string::String> {
let mut vec = alloc::vec::Vec::new();
let _ = self.encode_into_vec(src, &mut vec)?;
Some(unsafe { alloc::string::String::from_utf8_unchecked(vec) })
}
#[cfg(feature = "alloc")]
#[inline]
fn encode_into_vec<'a>(&self, src: &[u8], dst: &'a mut alloc::vec::Vec<u8>) -> Option<&'a str> {
let start = dst.len();
dst.extend(iter::repeat_n(0, self.encoded_len(src.len())?));
Some(self.encode_into_exact(src, &mut dst[start..]).unwrap())
}
#[cfg(feature = "alloc")]
#[inline]
fn encode_into_string<'a>(
&self,
src: &[u8],
dst: &'a mut alloc::string::String
) -> Option<&'a str> {
self.encode_into_vec(src, unsafe { dst.as_mut_vec() })
}
}
pub trait Decoder<const LEN: usize>: DynDecoder {
fn decoding_table(&self) -> &DecodingTable<LEN>;
}
pub trait DynDecoder {
fn dyn_decoding_table(&self) -> &DynDecodingTable;
fn decoded_len(&self, src: &[u8]) -> Option<usize>;
fn decode_into_exact<'a>(
&self,
src: &[u8],
dst: &'a mut [u8]
) -> Result<&'a [u8], DecodeIntoExactError>;
#[inline]
fn decode_into<'a>(&self, src: &[u8], dst: &'a mut [u8]) -> Result<&'a [u8], DecodeIntoError> {
let dst = dst
.get_mut(..self.decoded_len(src).ok_or(DecodeIntoError::InvalidLength)?)
.ok_or(DecodeIntoError::TooSmall)?;
match self.decode_into_exact(src, dst) {
Ok(s) => Ok(s),
Err(DecodeIntoExactError::InvalidCharacter(i)) => {
Err(DecodeIntoError::InvalidCharacter(i))
}
Err(DecodeIntoExactError::InvalidChunk { index, len }) => {
Err(DecodeIntoError::InvalidChunk { index, len })
}
Err(DecodeIntoExactError::NonCanonical) => Err(DecodeIntoError::NonCanonical),
Err(DecodeIntoExactError::InvalidLength | DecodeIntoExactError::LengthMismatch) => {
unreachable!()
}
}
}
#[cfg(feature = "alloc")]
#[inline]
fn decode_vec(&self, src: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeVecError> {
let mut vec = alloc::vec::Vec::new();
let _ = self.decode_into_vec(src, &mut vec)?;
Ok(vec)
}
#[cfg(feature = "alloc")]
#[inline]
fn decode_into_vec<'a>(
&self,
src: &[u8],
dst: &'a mut alloc::vec::Vec<u8>
) -> Result<&'a [u8], DecodeVecError> {
let start = dst.len();
dst.extend(iter::repeat_n(0, self.decoded_len(src).ok_or(DecodeVecError::InvalidLength)?));
match self.decode_into_exact(src, &mut dst[start..]) {
Ok(dst) => Ok(dst),
Err(DecodeIntoExactError::InvalidCharacter(i)) => {
Err(DecodeVecError::InvalidCharacter(i))
}
Err(DecodeIntoExactError::InvalidChunk { index, len }) => {
Err(DecodeVecError::InvalidChunk { index, len })
}
Err(DecodeIntoExactError::NonCanonical) => Err(DecodeVecError::NonCanonical),
Err(DecodeIntoExactError::InvalidLength | DecodeIntoExactError::LengthMismatch) => {
unreachable!()
}
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct ArrayString<const LEN: usize> {
bytes: [u8; LEN]
}
impl<const LEN: usize> ArrayString<LEN> {
pub const fn as_str(&self) -> &str {
unsafe { str::from_utf8_unchecked(&self.bytes) }
}
}
impl<const LEN: usize> Deref for ArrayString<LEN> {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl<const LEN: usize> AsRef<str> for ArrayString<LEN> {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl<const LEN: usize> AsRef<[u8; LEN]> for ArrayString<LEN> {
fn as_ref(&self) -> &[u8; LEN] {
&self.bytes
}
}
impl<const LEN: usize> AsRef<[u8]> for ArrayString<LEN> {
fn as_ref(&self) -> &[u8] {
&self.bytes
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum EncodeIntoError {
LengthOverflow,
TooSmall
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum EncodeIntoExactError {
LengthOverflow,
LengthMismatch
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum DecodeIntoError {
InvalidLength,
TooSmall,
InvalidCharacter(usize),
NonCanonical,
InvalidChunk { index: usize, len: usize }
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum DecodeIntoExactError {
InvalidLength,
LengthMismatch,
InvalidCharacter(usize),
NonCanonical,
InvalidChunk { index: usize, len: usize }
}
#[cfg(feature = "alloc")]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum DecodeVecError {
InvalidLength,
InvalidCharacter(usize),
NonCanonical,
InvalidChunk { index: usize, len: usize }
}
#[cfg(test)]
mod tests {
use super::*;
#[allow(dead_code)]
fn assert_dyn_safe(_: &dyn DynEncoder) {}
}