1use std::str;
2pub mod url_safe_util;
3pub mod macros;
4
5const BITS_DIFF: usize = 8 - 6;
6const PADDING_B4: u8 = 0x4d;
7
8
9pub fn encode(bytes: &[u8]) -> String {
10 let bytes: &[u8] = &encode_bytes(bytes);
11 str::from_utf8(bytes).unwrap().to_string()
12}
13
14pub fn encode_bytes(bytes: &[u8]) -> Box<[u8]> {
15 let result_length = encoded_length_for(bytes);
16
17 let mut result = Vec::with_capacity(result_length);
18 for index in 0..result_length {
19 let start_bit = index * 6;
20 let start_byte = start_bit >> 3;
21
22 let start_bit = (start_bit % 8) as u8;
23 let b64 = if start_byte >= bytes.len() {
24 PADDING_B4
25 } else if start_bit <= BITS_DIFF as u8 {
26 bytes[start_byte] >> (BITS_DIFF as u8 - start_bit) & 0b0011_1111
27 } else {
28 let mut result = (bytes[start_byte] & (0xff >> start_bit)) << start_bit - BITS_DIFF as u8;
29 if start_byte + 1 < bytes.len() {
30 result |= bytes[start_byte + 1] >> 10 - start_bit;
31 }
32 result
33 };
34
35 result.push(b64_to_ascii(b64));
36 }
37
38 result.into_boxed_slice()
39}
40
41pub fn decode(bytes: &[u8]) -> Box<[u8]> {
42 let potential_length = (bytes.len() * 6) >> 3;
43 let mut start_bit: u8 = 0;
44 let mut byte: u8 = 0;
45 let mut result: Vec<u8> = Vec::with_capacity(potential_length);
46
47 for index in 0..bytes.len() {
48 let ascii = bytes[index];
49
50 if ascii == b'=' {
51 break;
52 }
53
54 let b64 = ascii_to_b64(ascii);
55
56 if start_bit <= BITS_DIFF as u8 {
57 byte |= b64 << (BITS_DIFF as u8 - start_bit);
58 if start_bit == BITS_DIFF as u8 {
59 result.push(byte);
60 byte = 0;
61 }
62 } else {
63 byte |= b64 >> start_bit - BITS_DIFF as u8;
64 result.push(byte);
65 byte = b64 << (8 - start_bit + BITS_DIFF as u8);
66 }
67 start_bit = (start_bit + 6) % 8;
68 }
69
70 result.into_boxed_slice()
71}
72
73const fn b64_to_ascii(b64: u8) -> u8 {
74 if b64 < 26 {
75 b'A' + b64
76 } else if b64 < 52 {
77 b'a' + b64 - 26
78 } else if b64 < 62 {
79 b'0' + b64 - 52
80 } else if b64 == 62 {
81 b'+'
82 } else if b64 == 63 {
83 b'/'
84 } else {
85 b'='
86 }
87}
88
89const fn ascii_to_b64(ascii: u8) -> u8 {
90 if ascii >= b'a' {
91 26 + ascii - b'a'
92 } else if ascii >= b'A' {
93 ascii - b'A'
94 } else if ascii >= b'0' {
95 52 + ascii - b'0'
96 } else if ascii == b'+' {
97 62
98 } else if ascii == b'/' {
99 63
100 } else {
101 0xff
102 }
103}
104
105pub fn encoded_length_for(bytes: &[u8]) -> usize {
106 let bits_length = bytes.len() << 3;
107
108 let mut result_length = bits_length / 6;
109 if bits_length % 6 > 0 {
110 result_length += (8 - (bits_length % 6)) / BITS_DIFF;
111 }
112 result_length
113}
114
115
116#[cfg(test)]
117mod tests {
118 use std::str::from_utf8;
119
120 use super::*;
121
122 #[test]
123 fn test_result_length() {
124 assert_eq!(encoded_length_for(b"a"), 4);
125 assert_eq!(encoded_length_for(b"ab"), 4);
126 assert_eq!(encoded_length_for(b"abc"), 4);
127 assert_eq!(encoded_length_for(b"abcd"), 8);
128 assert_eq!(encoded_length_for(b"abcde"), 8);
129 assert_eq!(encoded_length_for(b"abcdef"), 8);
130 }
131
132 #[test]
133 fn test_encode() {
134 let base64 = encode("Ma".as_bytes());
135 assert_eq!(base64, "TWE=");
136
137 let base64 = encode("Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.".as_bytes());
138 assert_eq!(base64, "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=");
139
140 assert_eq!(
141 "XC5WGfrtRruAKSncMk+InwNcbkIX/v+jyz3YAqKMwiCtv9O5BbqsvlSDErnoEoyJCljGAWvqOHLLgP1BxxuZsg==",
142 encode([
143 0x5c, 0x2e, 0x56, 0x19, 0xfa, 0xed, 0x46, 0xbb, 0x80, 0x29, 0x29, 0xdc, 0x32, 0x4f, 0x88, 0x9f, 0x03, 0x5c,
144 0x6e, 0x42, 0x17, 0xfe, 0xff, 0xa3, 0xcb, 0x3d, 0xd8, 0x02, 0xa2, 0x8c, 0xc2, 0x20, 0xad, 0xbf, 0xd3, 0xb9,
145 0x05, 0xba, 0xac, 0xbe, 0x54, 0x83, 0x12, 0xb9, 0xe8, 0x12, 0x8c, 0x89, 0x0a, 0x58, 0xc6, 0x01, 0x6b, 0xea,
146 0x38, 0x72, 0xcb, 0x80, 0xfd, 0x41, 0xc7, 0x1b, 0x99, 0xb2
147 ].as_slice())
148 )
149 }
150
151 #[test]
152 fn test_b64_to_ascii() {
153 assert_eq!(b64_to_ascii(0), b'A');
154 assert_eq!(b64_to_ascii(9), b'J');
155 assert_eq!(b64_to_ascii(40), b'o');
156 assert_eq!(b64_to_ascii(51), b'z');
157 assert_eq!(b64_to_ascii(52), b'0');
158 assert_eq!(b64_to_ascii(61), b'9');
159 assert_eq!(b64_to_ascii(62), b'+');
160 assert_eq!(b64_to_ascii(63), b'/');
161 assert_eq!(b64_to_ascii(PADDING_B4), b'=');
162 }
163
164 #[test]
165 fn test_ascii_to_b64() {
166 assert_eq!(ascii_to_b64(b'A'), 0);
167 assert_eq!(ascii_to_b64(b'J'), 9);
168 assert_eq!(ascii_to_b64(b'o'), 40);
169 assert_eq!(ascii_to_b64(b'z'), 51);
170 assert_eq!(ascii_to_b64(b'0'), 52);
171 assert_eq!(ascii_to_b64(b'9'), 61);
172 assert_eq!(ascii_to_b64(b'+'), 62);
173 assert_eq!(ascii_to_b64(b'/'), 63);
174 }
176
177 #[test]
178 fn test_decode() {
179 let message = decode("TWE=".as_bytes());
180 assert_eq!(message.as_ref(), b"Ma");
181
182 let message = base64_decode!("TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=");
183 assert_eq!(from_utf8(&message).unwrap(), "Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.");
184
185 const DECODED: &[u8] = &base64_decode!("TWE=");
186 assert_eq!(DECODED, b"Ma");
187
188 const LONG_DECODED: &[u8] = &base64_decode!("TWFu");
189 assert_eq!(LONG_DECODED, b"Man");
190 assert_eq!(
191 decode(b"XC5WGfrtRruAKSncMk+InwNcbkIX/v+jyz3YAqKMwiCtv9O5BbqsvlSDErnoEoyJCljGAWvqOHLLgP1BxxuZsg==").as_ref(),
192 [
193 0x5c, 0x2e, 0x56, 0x19, 0xfa, 0xed, 0x46, 0xbb, 0x80, 0x29, 0x29, 0xdc, 0x32, 0x4f, 0x88, 0x9f, 0x03, 0x5c,
194 0x6e, 0x42, 0x17, 0xfe, 0xff, 0xa3, 0xcb, 0x3d, 0xd8, 0x02, 0xa2, 0x8c, 0xc2, 0x20, 0xad, 0xbf, 0xd3, 0xb9,
195 0x05, 0xba, 0xac, 0xbe, 0x54, 0x83, 0x12, 0xb9, 0xe8, 0x12, 0x8c, 0x89, 0x0a, 0x58, 0xc6, 0x01, 0x6b, 0xea,
196 0x38, 0x72, 0xcb, 0x80, 0xfd, 0x41, 0xc7, 0x1b, 0x99, 0xb2
197 ].as_slice()
198 )
199 }
200}