1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
use crate::alphabet::{self, SIZE, SIZE_SIZE};
use std::error::Error;
use std::fmt::{Display, Formatter};
/// The error type returned when the input is not a valid base45 string
#[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 {}

/// Decode a string from base45
///
/// Takes a base45 encoded string and returns a UTF-8 string on success
///
/// ```rust,no_run
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let decoded = String::from_utf8(base45::decode("%69 VD92EX0")?)?;
/// assert_eq!(decoded, "Hello!!");
/// # Ok(())
/// # }
/// ```
pub fn decode(input: impl AsRef<[u8]>) -> Result<Vec<u8>, DecodeError> {
    decode_intl(input.as_ref())
}
/// Internal function to decode a string from base45, to reduce code bloat.
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,
        // SAFETY: it's one of 0 or 1. There are no other options.
        _ => 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)
        }
    }
    //short
    #[inline(always)]
    fn preproc([a, b, c]: [u8; 3]) -> Result<[u8; 3], DecodeError> {
        Ok([d(a)?, d(b)?, d(c)?])
    }
    //short
    #[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]),
        // SAFETY: chunks_exact ensures every `.next()` returns an effective &[T] where `.len` == 3
        _ => 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)
}