use crate::alphabet::{self, SIZE, SIZE_SIZE};
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::fmt::{Display, Formatter};
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
pub struct DecodeError;
impl Display for DecodeError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.write_str("Invalid base45 string")
}
}
impl core::error::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,
#[cfg(feature = "unsafe")]
_ => unsafe { core::hint::unreachable_unchecked() },
#[cfg(not(feature = "unsafe"))]
_ => unreachable!(),
});
#[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)
}
let ((chunks, remainder), pre) = (input.as_chunks::<3>(), |&b| preproc(b));
for c in chunks.iter().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)
}