use crate::u6::U6;
use crate::{private::SealedConfig, Config, DecodeError};
use std::fmt;
macro_rules! impl_config_from_table {
($cfg:ty, $encode_table:ident, $decode_table:ident, $padding:expr) => {
impl SealedConfig for $cfg {
#[inline]
fn encode_u6(self, input: U6) -> u8 {
crate::encode::encode_using_table(crate::tables::$encode_table, input)
}
#[inline]
fn decode_u8(self, input: u8) -> u8 {
crate::decode::decode_using_table(crate::tables::$decode_table, input)
}
#[inline]
fn padding_byte(self) -> Option<u8> {
$padding
}
}
impl Config for $cfg {}
};
}
macro_rules! define_inherent_impl {
($cfg:ty) => {
impl $cfg {
#[inline]
pub fn encode<I>(self, input: &I) -> String
where
I: AsRef<[u8]> + ?Sized,
{
<Self as Config>::encode(self, input)
}
#[inline]
pub fn encode_with_buffer<'i, 'b, I>(
self,
input: &'i I,
buffer: &'b mut Vec<u8>,
) -> &'b str
where
I: AsRef<[u8]> + ?Sized,
{
<Self as Config>::encode_with_buffer(self, input, buffer)
}
#[inline]
pub fn encode_slice<I>(self, input: &I, output: &mut [u8]) -> usize
where
I: AsRef<[u8]> + ?Sized,
{
<Self as Config>::encode_slice(self, input, output)
}
#[inline]
pub fn decode<I>(self, input: &I) -> Result<Vec<u8>, DecodeError>
where
I: AsRef<[u8]> + ?Sized,
{
<Self as Config>::decode(self, input)
}
#[inline]
pub fn decode_with_buffer<'i, 'b, I>(
self,
input: &'i I,
buffer: &'b mut Vec<u8>,
) -> Result<&'b [u8], DecodeError>
where
I: AsRef<[u8]> + ?Sized,
{
<Self as Config>::decode_with_buffer(self, input, buffer)
}
#[inline]
pub fn decode_slice<I>(self, input: &I, output: &mut [u8]) -> Result<usize, DecodeError>
where
I: AsRef<[u8]> + ?Sized,
{
<Self as Config>::decode_slice(self, input, output)
}
}
};
}
#[derive(Debug, Clone, Copy)]
pub struct Std;
impl_config_from_table!(Std, STD_ENCODE, STD_DECODE, Some(b'='));
define_inherent_impl!(Std);
#[derive(Debug, Clone, Copy)]
pub struct StdNoPad;
impl_config_from_table!(StdNoPad, STD_ENCODE, STD_DECODE, None);
define_inherent_impl!(StdNoPad);
#[derive(Debug, Clone, Copy)]
pub struct UrlSafe;
impl_config_from_table!(UrlSafe, URL_SAFE_ENCODE, URL_SAFE_DECODE, Some(b'='));
define_inherent_impl!(UrlSafe);
#[derive(Debug, Clone, Copy)]
pub struct UrlSafeNoPad;
impl_config_from_table!(UrlSafeNoPad, URL_SAFE_ENCODE, URL_SAFE_DECODE, None);
define_inherent_impl!(UrlSafeNoPad);
#[derive(Debug, Clone, Copy)]
pub struct Crypt;
impl_config_from_table!(Crypt, CRYPT_ENCODE, CRYPT_DECODE, None);
define_inherent_impl!(Crypt);
#[derive(Debug, Clone, Copy)]
pub struct Fast;
impl_config_from_table!(Fast, FAST_ENCODE, FAST_DECODE, None);
define_inherent_impl!(Fast);
#[derive(Clone)]
pub struct CustomConfig {
encode_table: [u8; 64],
decode_table: [u8; 256],
padding_byte: Option<u8>,
}
impl SealedConfig for &CustomConfig {
fn encode_u6(self, input: U6) -> u8 {
crate::encode::encode_using_table(&self.encode_table, input)
}
fn decode_u8(self, input: u8) -> u8 {
crate::decode::decode_using_table(&self.decode_table, input)
}
fn padding_byte(self) -> Option<u8> {
self.padding_byte
}
}
impl Config for &CustomConfig {}
impl CustomConfig {
pub fn with_alphabet<A: AsRef<[u8]> + ?Sized>(alphabet: &A) -> CustomConfigBuilder {
CustomConfigBuilder::with_alphabet(alphabet)
}
#[inline]
pub fn encode<I>(&self, input: &I) -> String
where
I: AsRef<[u8]> + ?Sized,
{
<&Self as Config>::encode(self, input)
}
#[inline]
pub fn encode_with_buffer<'i, 'b, I>(&self, input: &'i I, buffer: &'b mut Vec<u8>) -> &'b str
where
I: AsRef<[u8]> + ?Sized,
{
<&Self as Config>::encode_with_buffer(self, input, buffer)
}
#[inline]
pub fn encode_slice<I>(&self, input: &I, output: &mut [u8]) -> usize
where
I: AsRef<[u8]> + ?Sized,
{
<&Self as Config>::encode_slice(self, input, output)
}
#[inline]
pub fn decode<I>(&self, input: &I) -> Result<Vec<u8>, DecodeError>
where
I: AsRef<[u8]> + ?Sized,
{
<&Self as Config>::decode(self, input)
}
#[inline]
pub fn decode_with_buffer<'i, 'b, I>(
&self,
input: &'i I,
buffer: &'b mut Vec<u8>,
) -> Result<&'b [u8], DecodeError>
where
I: AsRef<[u8]> + ?Sized,
{
<&Self as Config>::decode_with_buffer(self, input, buffer)
}
#[inline]
pub fn decode_slice<'a, 'b, I>(
&self,
input: &'a I,
output: &'b mut [u8],
) -> Result<usize, DecodeError>
where
I: AsRef<[u8]> + ?Sized,
{
<&Self as Config>::decode_slice(self, input, output)
}
}
impl fmt::Debug for CustomConfig {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("CustomConfig")
.field("encode_table", &&self.encode_table[..])
.field("decode_table", &&self.decode_table[..])
.field("padding_byte", &self.padding_byte)
.finish()
}
}
#[derive(Debug, Clone)]
pub struct CustomConfigBuilder<'a> {
alphabet: &'a [u8],
padding_byte: Option<u8>,
}
#[derive(Debug, Clone)]
pub enum CustomConfigError {
AlphabetNot64Bytes,
NonAscii(u8),
DuplicateValue(u8),
}
impl<'a> CustomConfigBuilder<'a> {
pub fn with_alphabet<A: AsRef<[u8]> + ?Sized>(alphabet: &'a A) -> Self {
CustomConfigBuilder {
alphabet: alphabet.as_ref(),
padding_byte: Some(b'='),
}
}
pub fn with_padding(mut self, padding_byte: u8) -> Self {
self.padding_byte = Some(padding_byte);
self
}
pub fn no_padding(mut self) -> Self {
self.padding_byte = None;
self
}
pub fn build(self) -> Result<CustomConfig, CustomConfigError> {
use crate::decode::INVALID_VALUE;
if self.alphabet.len() != 64 {
return Err(CustomConfigError::AlphabetNot64Bytes);
}
if let Some(&b) = self.alphabet.iter().find(|b| !b.is_ascii()) {
return Err(CustomConfigError::NonAscii(b));
}
if let Some(b) = self.padding_byte {
if !b.is_ascii() {
return Err(CustomConfigError::NonAscii(b));
}
if self.alphabet.iter().cloned().any(|c| c == b) {
return Err(CustomConfigError::DuplicateValue(b));
}
}
let mut decode_scratch: Vec<u8> = vec![INVALID_VALUE; 256];
for (i, b) in self.alphabet.iter().cloned().enumerate() {
if decode_scratch[b as usize] != INVALID_VALUE {
return Err(CustomConfigError::DuplicateValue(b));
}
decode_scratch[b as usize] = i as u8;
}
let mut encode_table = [0; 64];
let mut decode_table = [0; 256];
encode_table.copy_from_slice(self.alphabet);
decode_table.copy_from_slice(&decode_scratch);
Ok(CustomConfig {
encode_table,
decode_table,
padding_byte: self.padding_byte,
})
}
}