fleischwolf_core/
base64.rs1const ALPHABET: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
6
7pub fn encode(data: &[u8]) -> String {
9 let mut out = String::with_capacity(data.len().div_ceil(3) * 4);
10 for chunk in data.chunks(3) {
11 let b0 = chunk[0] as u32;
12 let b1 = *chunk.get(1).unwrap_or(&0) as u32;
13 let b2 = *chunk.get(2).unwrap_or(&0) as u32;
14 let n = (b0 << 16) | (b1 << 8) | b2;
15 out.push(ALPHABET[(n >> 18 & 0x3f) as usize] as char);
16 out.push(ALPHABET[(n >> 12 & 0x3f) as usize] as char);
17 out.push(if chunk.len() > 1 {
18 ALPHABET[(n >> 6 & 0x3f) as usize] as char
19 } else {
20 '='
21 });
22 out.push(if chunk.len() > 2 {
23 ALPHABET[(n & 0x3f) as usize] as char
24 } else {
25 '='
26 });
27 }
28 out
29}
30
31pub fn decode(s: &str) -> Option<Vec<u8>> {
35 let mut out = Vec::with_capacity(s.len() / 4 * 3);
36 let mut acc: u32 = 0;
37 let mut bits: u8 = 0;
38 for &c in s.as_bytes() {
39 let v = match c {
40 b'A'..=b'Z' => c - b'A',
41 b'a'..=b'z' => c - b'a' + 26,
42 b'0'..=b'9' => c - b'0' + 52,
43 b'+' => 62,
44 b'/' => 63,
45 b'=' => break,
46 b' ' | b'\t' | b'\r' | b'\n' => continue,
47 _ => return None,
48 };
49 acc = (acc << 6) | v as u32;
50 bits += 6;
51 if bits >= 8 {
52 bits -= 8;
53 out.push((acc >> bits) as u8);
54 }
55 }
56 Some(out)
57}
58
59#[cfg(test)]
60mod tests {
61 use super::{decode, encode};
62
63 #[test]
64 fn rfc4648_vectors() {
65 assert_eq!(encode(b""), "");
66 assert_eq!(encode(b"f"), "Zg==");
67 assert_eq!(encode(b"fo"), "Zm8=");
68 assert_eq!(encode(b"foo"), "Zm9v");
69 assert_eq!(encode(b"foob"), "Zm9vYg==");
70 assert_eq!(encode(b"fooba"), "Zm9vYmE=");
71 assert_eq!(encode(b"foobar"), "Zm9vYmFy");
72 }
73
74 #[test]
75 fn decode_roundtrips() {
76 for s in ["", "f", "fo", "foo", "foob", "fooba", "foobar"] {
77 assert_eq!(decode(&encode(s.as_bytes())).as_deref(), Some(s.as_bytes()));
78 }
79 assert_eq!(decode("Zm9vYmFy").as_deref(), Some(&b"foobar"[..]));
81 assert_eq!(decode("Zm9v\nYmE=").as_deref(), Some(&b"fooba"[..]));
82 assert_eq!(decode("Zg").as_deref(), Some(&b"f"[..]));
83 assert!(decode("not base64!").is_none());
84 }
85}