taba_client/base64/
mod.rs1const 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
30pub 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;