Skip to main content

ethrex_common/
base64.rs

1//! base64 decoder/encoder using safe alphabet according to:
2//! https://datatracker.ietf.org/doc/html/rfc4648#section-4
3//! https://datatracker.ietf.org/doc/html/rfc4648#section-5
4//!
5//! Encoding is implementing with padding at the end (add 1 or 2 '=' if necessary to make the data a multiple of 4)
6//! Decoding does not require the data to be padded, that is it makes no difference if padding is present or not
7
8pub(crate) fn byte_to_alphabet(byte: u8) -> char {
9    match byte {
10        0..=25 => (b'A' + byte) as char,         // A-Z
11        26..=51 => (b'a' + (byte - 26)) as char, // a-z
12        52..=61 => (b'0' + (byte - 52)) as char, // 0-9
13        62 => '-',
14        63 => '_',
15        _ => '\0',
16    }
17}
18
19pub(crate) fn alphabet_to_byte(byte: u8) -> u8 {
20    match byte {
21        b'A'..=b'Z' => byte - b'A',
22        b'a'..=b'z' => byte - b'a' + 26,
23        b'0'..=b'9' => byte - b'0' + 52,
24        b'-' => 62,
25        b'_' => 63,
26        b'=' => 64,
27        _ => 0,
28    }
29}
30
31pub fn encode(bytes: &[u8]) -> Vec<u8> {
32    let mut result: Vec<u8> = vec![];
33
34    let mut bytes_iter = bytes.iter();
35    while bytes_iter.len() > 0 {
36        // each block is made up of as many as 24 bits (3 bytes)
37        let mut block: Vec<u8> = vec![];
38
39        while block.len() < 3 {
40            if let Some(next) = bytes_iter.next() {
41                block.push(*next);
42            } else {
43                break;
44            }
45        }
46
47        let missing_bytes = 3 - block.len();
48
49        // divide each block in a group of 4 concatenated 6 bits
50        // and push its alphabet representation
51        let mut carry = 0;
52        let mut carry_bits: i32 = 0;
53        for byte in block {
54            let mut chunk = 0;
55            let bits_left = 6 - carry_bits;
56            if bits_left > 0 {
57                chunk = byte >> (8 - bits_left);
58            }
59            // concatenate bits
60            chunk |= carry << bits_left;
61            carry_bits = 8 - bits_left;
62            carry = byte & ((1 << carry_bits) - 1);
63            result.push(byte_to_alphabet(chunk) as u8);
64        }
65        let chunk = carry << (6 - carry_bits);
66        result.push(byte_to_alphabet(chunk) as u8);
67
68        if missing_bytes == 1 {
69            result.push(b'=');
70        }
71        if missing_bytes == 2 {
72            result.push(b'=');
73            result.push(b'=');
74        }
75    }
76
77    result
78}
79
80pub fn decode(bytes: &[u8]) -> Vec<u8> {
81    let mut result = vec![];
82    let mut carry_bits: u8 = 0;
83
84    for (i, byte) in bytes.iter().enumerate() {
85        let val = alphabet_to_byte(*byte);
86        if val == 64 {
87            break;
88        }
89
90        // this byte has been consumed, continue with the next one
91        if carry_bits == 6 {
92            carry_bits = 0;
93            continue;
94        }
95
96        let bit_1 = alphabet_to_byte(*byte) & ((1 << (6 - carry_bits)) - 1);
97        carry_bits = 8 - (6 - carry_bits);
98
99        // Check if there's another byte left
100        if i + 1 >= bytes.len() {
101            break;
102        }
103        let next_val = alphabet_to_byte(bytes[i + 1]);
104        if next_val == 64 {
105            break;
106        }
107        let bit_2 = next_val >> (6 - carry_bits);
108
109        let bits = (bit_1 << (carry_bits)) | bit_2;
110        result.push(bits);
111    }
112
113    result
114}