forked from opendevtools/base45
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdecode.rs
More file actions
77 lines (70 loc) · 2.38 KB
/
Copy pathdecode.rs
File metadata and controls
77 lines (70 loc) · 2.38 KB
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
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,
#[cfg(feature = "unsafe")]
// SAFETY: it's one of 0 or 1. There are no other options.
_ => 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)
}
}
//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)
}
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)
}