Skip to main content

mail_builder/encoders/
base64.rs

1/*
2 * Copyright Stalwart Labs Ltd. See the COPYING
3 * file at the top-level directory of this distribution.
4 *
5 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 * https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 * <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
8 * option. This file may not be copied, modified, or distributed
9 * except according to those terms.
10 */
11
12use std::io::{self, Write};
13
14const CHARPAD: u8 = b'=';
15
16#[inline(always)]
17pub fn base64_encode(input: &[u8]) -> io::Result<Vec<u8>> {
18    let mut buf = Vec::with_capacity(4 * (input.len() / 3));
19    base64_encode_mime(input, &mut buf, true)?;
20    Ok(buf)
21}
22
23pub fn base64_encode_mime(input: &[u8], mut output: impl Write, is_inline: bool) -> io::Result<usize> {
24    let mut i = 0;
25    let mut t1;
26    let mut t2;
27    let mut t3;
28    let mut bytes_written = 0;
29
30    if input.len() > 2 {
31        while i < input.len() - 2 {
32            #[cfg(not(feature = "ludicrous_mode"))]
33            {
34                t1 = input[i];
35                t2 = input[i + 1];
36                t3 = input[i + 2];
37
38                output.write_all(&[
39                    E0[t1 as usize],
40                    E1[(((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)) as usize],
41                    E1[(((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)) as usize],
42                    E2[t3 as usize],
43                ])?;
44            }
45
46            #[cfg(feature = "ludicrous_mode")]
47            unsafe {
48                t1 = *input.get_unchecked(i);
49                t2 = *input.get_unchecked(i + 1);
50                t3 = *input.get_unchecked(i + 2);
51
52                output.write_all(&[
53                    *E0.get_unchecked(t1 as usize),
54                    *E1.get_unchecked((((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)) as usize),
55                    *E1.get_unchecked((((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)) as usize),
56                    *E2.get_unchecked(t3 as usize),
57                ])?;
58            }
59
60            bytes_written += 4;
61
62            if !is_inline && bytes_written % 19 == 0 {
63                output.write_all(b"\r\n")?;
64            }
65
66            i += 3;
67        }
68    }
69
70    let remaining = input.len() - i;
71    if remaining > 0 {
72        #[cfg(not(feature = "ludicrous_mode"))]
73        {
74            t1 = input[i];
75            if remaining == 1 {
76                output.write_all(&[E0[t1 as usize], E1[((t1 & 0x03) << 4) as usize], CHARPAD, CHARPAD])?;
77            } else {
78                t2 = input[i + 1];
79                output.write_all(&[
80                    E0[t1 as usize],
81                    E1[(((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)) as usize],
82                    E2[((t2 & 0x0F) << 2) as usize],
83                    CHARPAD,
84                ])?;
85            }
86        }
87
88        #[cfg(feature = "ludicrous_mode")]
89        unsafe {
90            t1 = *input.get_unchecked(i);
91            if remaining == 1 {
92                output.write_all(&[
93                    *E0.get_unchecked(t1 as usize),
94                    *E1.get_unchecked(((t1 & 0x03) << 4) as usize),
95                    CHARPAD,
96                    CHARPAD,
97                ])?;
98            } else {
99                t2 = *input.get_unchecked(i + 1);
100                output.write_all(&[
101                    *E0.get_unchecked(t1 as usize),
102                    *E1.get_unchecked((((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)) as usize),
103                    *E2.get_unchecked(((t2 & 0x0F) << 2) as usize),
104                    CHARPAD,
105                ])?;
106            }
107        }
108
109        bytes_written += 4;
110
111        if !is_inline && bytes_written % 19 == 0 {
112            output.write_all(b"\r\n")?;
113        }
114    }
115
116    if !is_inline && bytes_written % 19 != 0 {
117        output.write_all(b"\r\n")?;
118    }
119
120    Ok(bytes_written)
121}
122
123#[cfg(test)]
124mod tests {
125
126    #[test]
127    fn encode_base64() {
128        for (input, expected_result, is_inline) in [
129            ("Test".to_string(), "VGVzdA==\r\n", false),
130            ("Ye".to_string(), "WWU=\r\n", false),
131            ("A".to_string(), "QQ==\r\n", false),
132            ("ro".to_string(), "cm8=\r\n", false),
133            (
134                "Are you a Shimano or Campagnolo person?".to_string(),
135                "QXJlIHlvdSBhIFNoaW1hbm8gb3IgQ2FtcGFnbm9sbyBwZXJzb24/\r\n",
136                false,
137            ),
138            (
139                "<!DOCTYPE html>\n<html>\n<body>\n</body>\n</html>\n".to_string(),
140                "PCFET0NUWVBFIGh0bWw+CjxodG1sPgo8Ym9keT4KPC9ib2R5Pgo8L2h0bWw+Cg==\r\n",
141                false,
142            ),
143            ("áéíóú".to_string(), "w6HDqcOtw7PDug==\r\n", false),
144            (
145                " ".repeat(100),
146                concat!(
147                    "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg",
148                    "ICAgICAgICAgICAgICAgICAgICAgICAgICAg\r\n",
149                    "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg",
150                    "ICAgICAgICAgICAgIA==\r\n",
151                ),
152                false,
153            ),
154        ] {
155            let mut output = Vec::new();
156            super::base64_encode_mime(input.as_bytes(), &mut output, is_inline).unwrap();
157            assert_eq!(std::str::from_utf8(&output).unwrap(), expected_result);
158        }
159    }
160}
161
162/*
163 * Table adapted from Nick Galbreath's "High performance base64 encoder / decoder"
164 *
165 * Copyright 2005, 2006, 2007 Nick Galbreath -- nickg [at] modp [dot] com
166 * All rights reserved.
167 *
168 * http://code.google.com/p/stringencoders/
169 *
170 * Released under bsd license.
171 *
172 */
173
174pub static E0: &[u8] = &[
175    b'A', b'A', b'A', b'A', b'B', b'B', b'B', b'B', b'C', b'C', b'C', b'C', b'D', b'D', b'D', b'D', b'E', b'E', b'E',
176    b'E', b'F', b'F', b'F', b'F', b'G', b'G', b'G', b'G', b'H', b'H', b'H', b'H', b'I', b'I', b'I', b'I', b'J', b'J',
177    b'J', b'J', b'K', b'K', b'K', b'K', b'L', b'L', b'L', b'L', b'M', b'M', b'M', b'M', b'N', b'N', b'N', b'N', b'O',
178    b'O', b'O', b'O', b'P', b'P', b'P', b'P', b'Q', b'Q', b'Q', b'Q', b'R', b'R', b'R', b'R', b'S', b'S', b'S', b'S',
179    b'T', b'T', b'T', b'T', b'U', b'U', b'U', b'U', b'V', b'V', b'V', b'V', b'W', b'W', b'W', b'W', b'X', b'X', b'X',
180    b'X', b'Y', b'Y', b'Y', b'Y', b'Z', b'Z', b'Z', b'Z', b'a', b'a', b'a', b'a', b'b', b'b', b'b', b'b', b'c', b'c',
181    b'c', b'c', b'd', b'd', b'd', b'd', b'e', b'e', b'e', b'e', b'f', b'f', b'f', b'f', b'g', b'g', b'g', b'g', b'h',
182    b'h', b'h', b'h', b'i', b'i', b'i', b'i', b'j', b'j', b'j', b'j', b'k', b'k', b'k', b'k', b'l', b'l', b'l', b'l',
183    b'm', b'm', b'm', b'm', b'n', b'n', b'n', b'n', b'o', b'o', b'o', b'o', b'p', b'p', b'p', b'p', b'q', b'q', b'q',
184    b'q', b'r', b'r', b'r', b'r', b's', b's', b's', b's', b't', b't', b't', b't', b'u', b'u', b'u', b'u', b'v', b'v',
185    b'v', b'v', b'w', b'w', b'w', b'w', b'x', b'x', b'x', b'x', b'y', b'y', b'y', b'y', b'z', b'z', b'z', b'z', b'0',
186    b'0', b'0', b'0', b'1', b'1', b'1', b'1', b'2', b'2', b'2', b'2', b'3', b'3', b'3', b'3', b'4', b'4', b'4', b'4',
187    b'5', b'5', b'5', b'5', b'6', b'6', b'6', b'6', b'7', b'7', b'7', b'7', b'8', b'8', b'8', b'8', b'9', b'9', b'9',
188    b'9', b'+', b'+', b'+', b'+', b'/', b'/', b'/', b'/',
189];
190
191pub static E1: &[u8] = &[
192    b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S',
193    b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l',
194    b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z', b'0', b'1', b'2', b'3', b'4',
195    b'5', b'6', b'7', b'8', b'9', b'+', b'/', b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L',
196    b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', b'a', b'b', b'c', b'd', b'e',
197    b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x',
198    b'y', b'z', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'+', b'/', b'A', b'B', b'C', b'D', b'E',
199    b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X',
200    b'Y', b'Z', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q',
201    b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9',
202    b'+', b'/', b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q',
203    b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j',
204    b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z', b'0', b'1', b'2',
205    b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'+', b'/',
206];
207
208pub static E2: &[u8] = &[
209    b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S',
210    b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l',
211    b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z', b'0', b'1', b'2', b'3', b'4',
212    b'5', b'6', b'7', b'8', b'9', b'+', b'/', b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L',
213    b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', b'a', b'b', b'c', b'd', b'e',
214    b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x',
215    b'y', b'z', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'+', b'/', b'A', b'B', b'C', b'D', b'E',
216    b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X',
217    b'Y', b'Z', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q',
218    b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9',
219    b'+', b'/', b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q',
220    b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j',
221    b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z', b'0', b'1', b'2',
222    b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'+', b'/',
223];