rspamd_base32/
encode.rs

1//! Base32 encoding routines
2use crate::alphabet::{Alphabet, ZBASE32, EncodeOrder};
3
4#[cfg(any(feature = "alloc", feature = "std", test))]
5use std::{string::String, vec};
6
7///Returns encoded length for given input length
8pub fn encoded_len(bytes_len: usize) -> Option<usize> {
9    let min_bytes = bytes_len / 5;
10    let rem = bytes_len % 5;
11    min_bytes.checked_mul(8).and_then(|c| c.checked_add(rem * 2 + 1))
12}
13
14///Encode base32 using the specified [Alphabet] and the predefined output slice.
15///Returns a `usize` of how many output bytes are filled.
16pub fn encode_alphabet_slice<T: AsRef<[u8]>>(
17    input: T,
18    output_buf: &mut [u8],
19    alphabet: &Alphabet,
20) -> usize {
21    let encode_table = alphabet.encode_symbols;
22    let input_bytes = input.as_ref();
23    let mut remain = -1_i32;
24    let mut o = 0_usize;
25
26    if alphabet.encode_order == EncodeOrder::OrderInversed {
27        for i in 0..input_bytes.len() {
28            remain = match i % 5 {
29                0 => {
30                    // 8 bits of input and 3 to remain
31                    let x = input_bytes[i] as i32;
32                    output_buf[o] = encode_table[(x & 0x1F) as usize];
33                    o = o + 1;
34                    x >> 5
35                },
36                1 => {
37                    // 11 bits of input, 1 to remain
38                    let inp = input_bytes[i] as i32;
39                    let x = remain | inp << 3;
40                    output_buf[o] = encode_table[(x & 0x1F) as usize];
41                    o = o + 1;
42                    output_buf[o] = encode_table[(x >> 5 & 0x1F) as usize];
43                    o = o + 1;
44                    x >> 10
45                }
46                2 => {
47                    // 9 bits of input, 4 to remain
48                    let inp = input_bytes[i] as i32;
49                    let x = remain | inp << 1;
50                    output_buf[o] = encode_table[(x & 0x1F) as usize];
51                    o = o + 1;
52                    x >> 5
53                },
54                3 => {
55                    // 12 bits of input, 2 to remain
56                    let inp = input_bytes[i] as i32;
57                    let x = remain | inp << 4;
58                    output_buf[o] = encode_table[(x & 0x1F) as usize];
59                    o = o + 1;
60                    output_buf[o] = encode_table[(x >> 5 & 0x1F) as usize];
61                    o = o + 1;
62                    x >> 10 & 0x3
63                },
64                4 => {
65                    // 10 bits of output, nothing to remain
66                    let inp = input_bytes[i] as i32;
67                    let x = remain | inp << 2;
68                    output_buf[o] = encode_table[(x & 0x1F) as usize];
69                    o = o + 1;
70                    output_buf[o] = encode_table[(x >> 5 & 0x1F) as usize];
71                    o = o + 1;
72                    -1
73                },
74                _ => unreachable!("Impossible remainder"),
75            };
76        }
77    }
78    else {
79        for i in 0..input_bytes.len() {
80            remain = match i % 5 {
81                0 => {
82                    // 8 bits of input and 3 to remain
83                    let inp = input_bytes[i] as i32;
84                    let x = inp >> 3;
85                    output_buf[o] = encode_table[(x & 0x1F) as usize];
86                    o = o + 1;
87                    (inp & 7) << 2
88                },
89                1 => {
90                    // 11 bits of input, 1 to remain
91                    let inp = input_bytes[i] as i32;
92                    let x = (remain << 6) | inp;
93                    output_buf[o] = encode_table[(x >> 6 & 0x1F) as usize];
94                    o = o + 1;
95                    output_buf[o] = encode_table[(x >> 1 & 0x1F) as usize];
96                    o = o + 1;
97                    (x & 0x1) << 4
98                }
99                2 => {
100                    // 9 bits of input, 4 to remain
101                    let inp = input_bytes[i] as i32;
102                    let x = (remain << 4) | inp;
103                    output_buf[o] = encode_table[(x >> 4 & 0x1F) as usize];
104                    o = o + 1;
105                    (x & 15) << 1
106                },
107                3 => {
108                    // 12 bits of input, 2 to remain\
109                    let inp = input_bytes[i] as i32;
110                    let x = remain << 7 | inp;
111                    output_buf[o] = encode_table[(x >> 7 & 0x1F) as usize];
112                    o = o + 1;
113                    output_buf[o] = encode_table[(x >> 2 & 0x1F) as usize];
114                    o = o + 1;
115                    (x & 3) << 3
116                },
117                4 => {
118                    // 10 bits of output, nothing to remain
119                    let inp = input_bytes[i] as i32;
120                    let x = remain << 5 | inp;
121                    output_buf[o] = encode_table[(x >> 5 & 0x1F) as usize];
122                    o = o + 1;
123                    output_buf[o] = encode_table[(x & 0x1F) as usize];
124                    o = o + 1;
125                    -1
126                },
127                _ => unreachable!("Impossible remainder"),
128            };
129        }
130    }
131
132    if remain >= 0 {
133        output_buf[o] = encode_table[(remain & 0x1F) as usize];
134        o = o + 1;
135    }
136
137    o
138}
139
140///Encode base32 using the specified [Alphabet].
141///Returns a `String`.
142///
143///# Example
144///
145///```rust
146///extern crate rspamd_base32;
147///
148///fn main() {
149///    let encoded = rspamd_base32::encode_alphabet(
150///        "hello",
151///        &rspamd_base32::alphabet::RFC,
152///    );
153///    println!("{}", encoded);
154///    // Prints 'NBSWY3DP'
155///}
156///```
157#[cfg(any(feature = "alloc", feature = "std", test))]
158pub fn encode_alphabet<T: AsRef<[u8]>>(input: T, alphabet: &Alphabet) -> String {
159    let encoded_size = encoded_len(input.as_ref().len())
160        .expect("usize overflow when calculating buffer size");
161    let mut buf = vec![0; encoded_size];
162    let enc_len = encode_alphabet_slice(input, &mut buf[..], alphabet);
163    String::from_utf8(buf[0..enc_len].to_owned()).expect("Invalid UTF8")
164}
165
166///Encode base32 using the default alphabet
167///Returns a `String` result
168///
169///# Example
170///
171///```rust
172///extern crate rspamd_base32;
173///
174///fn main() {
175///    let encoded = rspamd_base32::encode("hello");
176///    println!("{}", encoded);
177///    // Prints 'em3ags7p'
178///}
179///```
180#[cfg(any(feature = "alloc", feature = "std", test))]
181pub fn encode<T: AsRef<[u8]>>(input: T) -> String {
182    encode_alphabet(input, &ZBASE32)
183}
184
185#[cfg(test)]
186mod tests {
187    use crate::encode::*;
188    use crate::alphabet::*;
189
190    #[test]
191    fn simple_encode_zbase() {
192        assert_eq!(
193            "wm3g84fg13cy",
194            encode("test123"),
195        );
196        assert_eq!(
197            "em3ags7p",
198            encode("hello"),
199        );
200    }
201    #[test]
202    fn empty_encode_zbase() {
203        assert_eq!(
204            "",
205            encode(""),
206        );
207    }
208    #[test]
209    fn series_encode_zbase() {
210        assert_eq!(
211            "bd",
212            encode("a"),
213        );
214        assert_eq!(
215            "bmay",
216            encode("aa"),
217        );
218        assert_eq!(
219            "bmang",
220            encode("aaa"),
221        );
222        assert_eq!(
223            "bmansob",
224            encode("aaaa"),
225        );
226        assert_eq!(
227            "bmansofc",
228            encode("aaaaa"),
229        );
230        assert_eq!(
231            "bmansofcbd",
232            encode("aaaaaa"),
233        );
234        assert_eq!(
235            "bmansofcbmay",
236            encode("aaaaaaa"),
237        );
238        assert_eq!(
239            "bmansofcbmang",
240            encode("aaaaaaaa"),
241        );
242    }
243    #[test]
244    fn simple_encode_rfc() {
245        assert_eq!(
246            "ORSXG5BRGIZQ",
247            encode_alphabet("test123", &RFC),
248        );
249        assert_eq!(
250            "NBSWY3DP",
251            encode_alphabet("hello", &RFC),
252        );
253    }
254    #[test]
255    fn empty_encode_rfc() {
256        assert_eq!(
257            "",
258            encode_alphabet("", &RFC),
259        );
260    }
261    #[test]
262    fn series_encode_rfc() {
263        assert_eq!(
264            "ME",
265            encode_alphabet("a", &RFC),
266        );
267        assert_eq!(
268            "MFQQ",
269            encode_alphabet("aa", &RFC),
270        );
271        assert_eq!(
272            "MFQWC",
273            encode_alphabet("aaa", &RFC),
274        );
275        assert_eq!(
276            "MFQWCYI",
277            encode_alphabet("aaaa", &RFC),
278        );
279        assert_eq!(
280            "MFQWCYLB",
281            encode_alphabet("aaaaa", &RFC),
282        );
283        assert_eq!(
284            "MFQWCYLBME",
285            encode_alphabet("aaaaaa", &RFC),
286        );
287        assert_eq!(
288            "MFQWCYLBMFQQ",
289            encode_alphabet("aaaaaaa", &RFC),
290        );
291        assert_eq!(
292            "MFQWCYLBMFQWC",
293            encode_alphabet("aaaaaaaa", &RFC),
294        );
295    }
296}