email_encoding/body/
base64.rs1use std::fmt::{self, Write};
4use std::str;
5
6use ::base64::Engine;
7
8const LINE_LEN: usize = 76;
9const CRLF: &str = "\r\n";
10
11pub fn encode(b: &[u8], w: &mut dyn Write) -> fmt::Result {
43 let mut buf = [0; LINE_LEN];
44
45 let mut chunks = b.chunks(LINE_LEN / 4 * 3).peekable();
46 while let Some(chunk) = chunks.next() {
47 let len = ::base64::engine::general_purpose::STANDARD
48 .encode_slice(chunk, &mut buf)
49 .expect("base64 output `buf` is not big enough");
50
51 w.write_str(str::from_utf8(&buf[..len]).expect("base64 produced an invalid encode"))?;
52 if chunks.peek().is_some() {
53 w.write_str(CRLF)?;
54 }
55 }
56
57 Ok(())
58}
59
60pub fn encoded_len(input_len: usize) -> usize {
71 let mut base64_len = input_len / 3 * 4;
72 if input_len % 3 != 0 {
73 base64_len += 4;
74 }
75 let mut crlf_len = base64_len / LINE_LEN * CRLF.len();
76 if crlf_len >= CRLF.len() && base64_len % LINE_LEN == 0 {
77 crlf_len -= CRLF.len();
78 }
79 base64_len + crlf_len
80}
81
82#[cfg(test)]
83mod tests {
84 use pretty_assertions::assert_eq;
85
86 use super::{encode, encoded_len};
87
88 #[test]
89 fn empty() {
90 let input = b"";
91 let mut output = String::new();
92
93 encode(input, &mut output).unwrap();
94
95 assert_eq!(output, "");
96 assert_eq!(output.len(), encoded_len(input.len()));
97 }
98
99 #[test]
100 fn oneline() {
101 let input = b"012";
102 let mut output = String::new();
103
104 encode(input, &mut output).unwrap();
105
106 assert_eq!(output, "MDEy");
107 assert_eq!(output.len(), encoded_len(input.len()));
108 }
109
110 #[test]
111 fn oneline_padded() {
112 let input = b"0123";
113 let mut output = String::new();
114
115 encode(input, &mut output).unwrap();
116
117 assert_eq!(output, "MDEyMw==");
118 assert_eq!(output.len(), encoded_len(input.len()));
119 }
120
121 #[test]
122 fn multiline() {
123 let input =
124 b"012345678998765432100123456789987654321001234567899876543210012345678998765432100";
125 let mut output = String::new();
126
127 encode(input, &mut output).unwrap();
128
129 assert_eq!(
130 output,
131 concat!(
132 "MDEyMzQ1Njc4OTk4NzY1NDMyMTAwMTIzNDU2Nzg5OTg3NjU0MzIxMDAxMjM0NTY3ODk5ODc2NTQz\r\n",
133 "MjEwMDEyMzQ1Njc4OTk4NzY1NDMyMTAw"
134 )
135 );
136 assert_eq!(output.len(), encoded_len(input.len()));
137 }
138
139 #[test]
140 fn multiline_padded() {
141 let input =
142 b"01234567899876543210012345678998765432100123456789987654321001234567899876543210";
143 let mut output = String::new();
144
145 encode(input, &mut output).unwrap();
146
147 assert_eq!(
148 output,
149 concat!(
150 "MDEyMzQ1Njc4OTk4NzY1NDMyMTAwMTIzNDU2Nzg5OTg3NjU0MzIxMDAxMjM0NTY3ODk5ODc2NTQz\r\n",
151 "MjEwMDEyMzQ1Njc4OTk4NzY1NDMyMTA="
152 )
153 );
154 assert_eq!(output.len(), encoded_len(input.len()));
155 }
156
157 #[test]
158 fn multiline_exact() {
159 let input =
160 b"012345678998765432100123456789987654321001234567899876543210012345678998765432100123456789987654321001234567899876543210012345678998765432100123456789987654321001234567899";
161 let mut output = String::new();
162
163 encode(input, &mut output).unwrap();
164
165 assert_eq!(
166 output,
167 concat!(
168 "MDEyMzQ1Njc4OTk4NzY1NDMyMTAwMTIzNDU2Nzg5OTg3NjU0MzIxMDAxMjM0NTY3ODk5ODc2NTQz\r\n",
169 "MjEwMDEyMzQ1Njc4OTk4NzY1NDMyMTAwMTIzNDU2Nzg5OTg3NjU0MzIxMDAxMjM0NTY3ODk5ODc2\r\n",
170 "NTQzMjEwMDEyMzQ1Njc4OTk4NzY1NDMyMTAwMTIzNDU2Nzg5OTg3NjU0MzIxMDAxMjM0NTY3ODk5"
171 )
172 );
173 assert_eq!(output.len(), encoded_len(input.len()));
174 }
175}