Skip to main content

taba_client/base64/
mod.rs

1//! Standard Base64 (RFC 4648), our own: the connector depends on no library for this.
2
3const ALPHABET: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4
5pub fn encode(data: &[u8]) -> String {
6    let mut out = String::with_capacity(data.len().div_ceil(3) * 4);
7    for chunk in data.chunks(3) {
8        let b = [
9            chunk[0],
10            *chunk.get(1).unwrap_or(&0),
11            *chunk.get(2).unwrap_or(&0),
12        ];
13        let n = (u32::from(b[0]) << 16) | (u32::from(b[1]) << 8) | u32::from(b[2]);
14        out.push(ALPHABET[(n >> 18) as usize & 63] as char);
15        out.push(ALPHABET[(n >> 12) as usize & 63] as char);
16        out.push(if chunk.len() > 1 {
17            ALPHABET[(n >> 6) as usize & 63] as char
18        } else {
19            '='
20        });
21        out.push(if chunk.len() > 2 {
22            ALPHABET[n as usize & 63] as char
23        } else {
24            '='
25        });
26    }
27    out
28}
29
30/// Decodes base64 with or without padding; returns `None` if there is a character outside the alphabet or
31/// the length is impossible.
32pub fn decode(text: &str) -> Option<Vec<u8>> {
33    let trimmed = text.trim_end_matches('=');
34    if text.len() - trimmed.len() > 2 || trimmed.len() % 4 == 1 {
35        return None;
36    }
37    let mut out = Vec::with_capacity(trimmed.len() * 3 / 4);
38    let mut acc: u32 = 0;
39    let mut bits = 0;
40    for c in trimmed.bytes() {
41        let v = match c {
42            b'A'..=b'Z' => c - b'A',
43            b'a'..=b'z' => c - b'a' + 26,
44            b'0'..=b'9' => c - b'0' + 52,
45            b'+' => 62,
46            b'/' => 63,
47            _ => return None,
48        };
49        acc = (acc << 6) | u32::from(v);
50        bits += 6;
51        if bits >= 8 {
52            bits -= 8;
53            out.push((acc >> bits) as u8);
54            acc &= (1 << bits) - 1;
55        }
56    }
57    Some(out)
58}
59
60#[cfg(test)]
61mod tests;