Skip to main content

spyne_encoding/transfer/
base64.rs

1static LOOKUP: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2static REVERSE_LOOKUP: [u8; 128] = reverse_lookup();
3const fn reverse_lookup() -> [u8; 128] {
4    let mut rl: [u8; 128] = [0xFF; 128];
5    let mut idx: usize = 0;
6    while idx < 64 {
7        rl[LOOKUP[idx] as usize] = idx as u8;
8        idx += 1;
9    }
10    
11    rl
12}
13
14pub fn encode(bytes: &[u8]) -> String {
15    let mut buffer = String::new();
16    encode_into(bytes, &mut buffer);
17    
18    buffer
19}
20
21pub fn encode_into(bytes: &[u8], buffer: &mut String) {
22    let mut chunks = bytes.chunks_exact(3);
23    chunks.by_ref().for_each(|ch| {
24        let byte_num = (ch[0] as u32) << 16 | (ch[1] as u32) << 8 | ch[2] as u32;
25        let chunk1 = (byte_num >> 18) & 0x3F;
26        let chunk2 = (byte_num >> 12) & 0x3F;
27        let chunk3 = (byte_num >> 6) & 0x3F;
28        let chunk4 = (byte_num) & 0x3F;
29        
30        [chunk1, chunk2, chunk3, chunk4].iter()
31            .for_each(|ch| {
32                buffer.push(LOOKUP[*ch as usize] as char);
33            });
34    });
35    let remainder = chunks.remainder();
36    match remainder.len() {
37        1 => {
38            let chunk1 = (remainder[0] >> 2) & 0x3F;
39            let chunk2 = (remainder[0] & 0x3) << 4;
40            [chunk1, chunk2].iter()
41                .for_each(|ch| {
42                    buffer.push(LOOKUP[*ch as usize] as char);
43                });
44            buffer.push_str("==");
45        },
46        2 => {
47            let byte_num = (remainder[0] as u16) << 8 | remainder[1] as u16;
48            let chunk1 = (byte_num >> 10) & 0x3F;
49            let chunk2 = (byte_num >> 4) & 0x3F;
50            let chunk3 = ((byte_num) & 0xF) << 2;
51            [chunk1, chunk2, chunk3].iter()
52                .for_each(|ch| {
53                    buffer.push(LOOKUP[*ch as usize] as char);
54                });
55            buffer.push('=');
56        },
57        _ => unreachable!()
58    }
59}
60
61pub fn decode(string: String) -> Vec<u8> {
62    let mut buffer = Vec::new();
63    decode_into(string, &mut buffer);
64    
65    buffer
66}
67
68pub fn decode_into(string: String, buffer: &mut Vec<u8>) {
69    let string_bytes = string.as_bytes();
70    let len = string_bytes.len();
71    let mut chunks = string_bytes[..len - 4].chunks_exact(4);
72    chunks.by_ref().for_each(|ch| {
73        let chunk1 = REVERSE_LOOKUP[ch[0] as usize];
74        let chunk2 = REVERSE_LOOKUP[ch[1] as usize];
75        let chunk3 = REVERSE_LOOKUP[ch[2] as usize];
76        let chunk4 = REVERSE_LOOKUP[ch[3] as usize];
77        let byte_num = (chunk1 as u32) << 18 | (chunk2 as u32) << 12 | (chunk3 as u32) << 6 | chunk4 as u32;
78        let byte1 = ((byte_num >> 16) & 0xFF) as u8;
79        let byte2 = ((byte_num >> 8) & 0xFF) as u8;
80        let byte3 = (byte_num & 0xFF) as u8;
81        buffer.extend([byte1, byte2, byte3]);
82    });
83    let last_chunk = &string_bytes[len - 4..len];
84    if last_chunk[2] == b'=' && last_chunk[3] == b'=' {
85        let chunk1 = REVERSE_LOOKUP[last_chunk[0] as usize];
86        let chunk2 = REVERSE_LOOKUP[last_chunk[1] as usize];
87        let byte_num = ((chunk1 as u32) << 6 | chunk2 as u32) >> 4;
88        let byte1 = (byte_num & 0xFF) as u8;
89        buffer.push(byte1);
90    }
91    else if last_chunk[3] == b'=' {
92        let chunk1 = REVERSE_LOOKUP[last_chunk[0] as usize];
93        let chunk2 = REVERSE_LOOKUP[last_chunk[1] as usize];
94        let chunk3 = REVERSE_LOOKUP[last_chunk[2] as usize];
95        let byte_num = ((chunk1 as u32) << 12 | (chunk2 as u32) << 6 | chunk3 as u32) >> 2;
96        let byte1 = ((byte_num >> 8) & 0xFF) as u8;
97        let byte2 = (byte_num & 0xFF) as u8;
98        buffer.extend([byte1, byte2]);
99    }
100    else {
101        let chunk1 = REVERSE_LOOKUP[last_chunk[0] as usize];
102        let chunk2 = REVERSE_LOOKUP[last_chunk[1] as usize];
103        let chunk3 = REVERSE_LOOKUP[last_chunk[2] as usize];
104        let chunk4 = REVERSE_LOOKUP[last_chunk[3] as usize];
105        let byte_num = (chunk1 as u32) << 18 | (chunk2 as u32) << 12 | (chunk3 as u32) << 6 | chunk4 as u32;
106        let byte1 = ((byte_num >> 16) & 0xFF) as u8;
107        let byte2 = ((byte_num >> 8) & 0xFF) as u8;
108        let byte3 = (byte_num & 0xFF) as u8;
109        buffer.extend([byte1, byte2, byte3]);
110    }
111}
112
113#[cfg(test)]
114mod test {
115    use crate::transfer::base64::{decode, encode};
116
117    #[test]
118    fn test_base64_encoder() {
119        let encoded = encode(b"hello");
120        assert_eq!(encoded, "aGVsbG8=");
121        let decoded = decode("aGVsbG8=".to_string());
122        assert_eq!(decoded, b"hello");
123    }
124}