use crate::alphabet::{self, SIZE, SIZE_SIZE};
use std::error::Error;
use std::fmt::{Display, Formatter};
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
pub struct DecodeError;
impl Display for DecodeError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str("Invalid base45 string")
}
}
impl Error for DecodeError {}
pub fn decode(input: impl AsRef<[u8]>) -> Result<Vec<u8>, DecodeError> {
decode_intl(input.as_ref())
}
fn decode_intl(input: &[u8]) -> Result<Vec<u8>, DecodeError> {
let mut output = Vec::with_capacity(match input.len() & 1 {
0 => input.len() * 2 / 3,
1 => 1 + input.len() * 2 / 3,
_ => unsafe { core::hint::unreachable_unchecked() },
});
#[inline(always)]
fn core_fn([_first, _second, _third]: [u8; 3]) -> Result<[u8; 2], DecodeError> {
let v = (_first as u32) + (_second as u32 * SIZE) + (_third as u32) * SIZE_SIZE;
if v <= (u16::MAX as _) {
let x = (v >> 8) & 0xFF;
let y = v & 0xFF;
Ok([x as u8, y as u8])
} else {
Err(DecodeError)
}
}
#[inline(always)]
fn preproc([a, b, c]: [u8; 3]) -> Result<[u8; 3], DecodeError> {
Ok([d(a)?, d(b)?, d(c)?])
}
#[inline(always)]
fn d(v: u8) -> Result<u8, DecodeError> {
alphabet::decode(v).ok_or(DecodeError)
}
#[cfg(feature = "array_chunks")]
let (chunks, pre) = (input.array_chunks(), |&b| preproc(b));
#[cfg(not(feature = "array_chunks"))]
let (chunks, pre) = (input.chunks_exact(3), |slic: &[u8]| match slic {
&[a, b, c] => preproc([a, b, c]),
_ => unsafe { core::hint::unreachable_unchecked() },
});
let remainder = chunks.remainder();
for c in chunks.map(pre) {
let xy = core_fn(c?)?;
output.extend_from_slice(&xy);
}
match remainder {
[_first, _second] => output.push((d(*_first)? as u32 + (d(*_second)? as u32 * SIZE)) as u8),
[] => {}
_ => return Err(DecodeError),
}
Ok(output)
}