use std::ops::{Add, Div, Mul, Rem};
use hmac::digest::generic_array::typenum::*;
use base64;
pub use base64::DecodeError;
use hmac::digest::generic_array::{ArrayLength, GenericArray};
static BASE64_ALPHABET: &'static str =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_=";
pub trait URLSafeBase64Encode: Sized {
fn base64_encode(self) -> String {
let mut target = String::new();
self.base64_encode_str(&mut target);
target
}
fn base64_encode_str(self, target: &mut String);
}
#[inline(always)]
pub(crate) fn encode<T>(input: &T) -> String
where
T: ?Sized + AsRef<[u8]>,
{
base64::encode_config(input, base64::URL_SAFE_NO_PAD)
}
#[inline(always)]
pub(crate) fn encode_slice<T>(input: &T, target: &mut [u8]) -> usize
where
T: ?Sized + AsRef<[u8]>,
{
base64::encode_config_slice(input, base64::URL_SAFE_NO_PAD, target)
}
#[inline(always)]
pub(crate) fn encode_str<T>(input: &T, target: &mut String)
where
T: ?Sized + AsRef<[u8]>,
{
base64::encode_config_buf(input, base64::URL_SAFE_NO_PAD, target)
}
pub(crate) struct DecodeResult<N: ArrayLength<u8>> {
array: GenericArray<u8, N>,
length: usize,
}
impl<N: ArrayLength<u8>> DecodeResult<N> {
pub(crate) fn as_slice(&self) -> &[u8] {
&self.array[..self.length]
}
pub(crate) fn into_exact_inner(self) -> Result<GenericArray<u8, N>, DecodeError> {
if self.array.len() != self.length {
Err(DecodeError::InvalidLength)
} else {
Ok(self.array)
}
}
}
#[inline(always)]
pub(crate) fn decode<N, T>(input: &T) -> Result<DecodeResult<N>, DecodeError>
where
N: ArrayLength<u8>,
T: ?Sized + AsRef<[u8]>,
{
let mut array = GenericArray::default();
let length = base64::decode_config_slice(input, base64::URL_SAFE_NO_PAD, &mut array)?;
Ok(DecodeResult { array, length })
}
#[inline(always)]
pub(crate) fn decode_str<T>(input: &T) -> Result<Vec<u8>, DecodeError>
where
T: ?Sized + AsRef<[u8]>,
{
base64::decode_config(input, base64::URL_SAFE_NO_PAD)
}
pub(crate) fn in_alphabet(c: char) -> bool {
BASE64_ALPHABET.contains(c)
}
pub trait Base64Sized {
type InputSize: ArrayLength<u8>;
type OutputSize: ArrayLength<u8>;
fn encode(input: GenericArray<u8, Self::InputSize>) -> GenericArray<u8, Self::OutputSize>;
fn output_size() -> usize;
}
pub struct Base64SizedEncoder<N>(N);
impl<N> Base64Sized for Base64SizedEncoder<N>
where
N: Unsigned + ArrayLength<u8>,
N: Rem<U3>,
N: Div<U3>,
Quot<N, U3>: Mul<U4>,
Mod<N, U3>: Min<U1>,
Mod<N, U3>: Add<Minimum<Mod<N, U3>, U1>>,
Prod<Quot<N, U3>, U4>: Add<Sum<Mod<N, U3>, Minimum<Mod<N, U3>, U1>>>,
Sum<Prod<Quot<N, U3>, U4>, Sum<Mod<N, U3>, Minimum<Mod<N, U3>, U1>>>:
Unsigned + ArrayLength<u8>,
{
type InputSize = N;
type OutputSize = Sum<Prod<Quot<N, U3>, U4>, Sum<Mod<N, U3>, Minimum<Mod<N, U3>, U1>>>;
fn encode(input: GenericArray<u8, Self::InputSize>) -> GenericArray<u8, Self::OutputSize> {
let mut output = GenericArray::default();
let size = encode_slice(input.as_slice(), output.as_mut_slice());
debug_assert_eq!(size, Self::OutputSize::to_usize());
output
}
fn output_size() -> usize {
Self::OutputSize::to_usize()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_base64_sized_encoder() {
fn gen_string<N: ArrayLength<u8>>() -> GenericArray<u8, N> {
let mut output: GenericArray<_, _> = Default::default();
for c in output.as_mut_slice() {
*c = 'a' as u8;
}
output
}
assert_eq!(Base64SizedEncoder::<U1>::output_size(), 2);
assert_eq!(
Base64SizedEncoder::encode(gen_string::<U1>()).as_slice(),
&[89, 81]
);
assert_eq!(
Base64SizedEncoder::encode(gen_string::<U2>()).as_slice(),
&[89, 87, 69]
);
assert_eq!(
Base64SizedEncoder::encode(gen_string::<U3>()).as_slice(),
&[89, 87, 70, 104]
);
assert_eq!(
Base64SizedEncoder::encode(gen_string::<U4>()).as_slice(),
&[89, 87, 70, 104, 89, 81]
);
assert_eq!(
Base64SizedEncoder::encode(gen_string::<U5>()).as_slice(),
&[89, 87, 70, 104, 89, 87, 69]
);
assert_eq!(
Base64SizedEncoder::encode(gen_string::<U6>()).as_slice(),
&[89, 87, 70, 104, 89, 87, 70, 104]
);
assert_eq!(
Base64SizedEncoder::encode(gen_string::<U7>()).as_slice(),
&[89, 87, 70, 104, 89, 87, 70, 104, 89, 81]
);
assert_eq!(
Base64SizedEncoder::encode(gen_string::<U8>()).as_slice(),
&[89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 69]
);
assert_eq!(
Base64SizedEncoder::encode(gen_string::<U9>()).as_slice(),
&[89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104]
);
assert_eq!(
Base64SizedEncoder::encode(gen_string::<U10>()).as_slice(),
&[89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 81]
);
assert_eq!(
Base64SizedEncoder::encode(gen_string::<U11>()).as_slice(),
&[89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 69]
);
assert_eq!(
Base64SizedEncoder::encode(gen_string::<U12>()).as_slice(),
&[89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104]
);
assert_eq!(
Base64SizedEncoder::encode(gen_string::<U13>()).as_slice(),
&[89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 81]
);
assert_eq!(
Base64SizedEncoder::encode(gen_string::<U14>()).as_slice(),
&[89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 69]
);
assert_eq!(
Base64SizedEncoder::encode(gen_string::<U15>()).as_slice(),
&[89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104]
);
}
}