Skip to main content

mail_parser/decoders/
base64.rs

1/*
2 * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
3 *
4 * SPDX-License-Identifier: Apache-2.0 OR MIT
5 */
6
7use crate::parsers::MessageStream;
8use std::borrow::Cow;
9
10const B64_INVALID: u8 = 0xff;
11#[cfg(feature = "base64_slice")]
12const B64_STOP_SCAN: usize = 512;
13
14static BASE64_DECODE: [u8; 256] = base64_decode_table();
15
16const fn base64_decode_table() -> [u8; 256] {
17    let mut table = [B64_INVALID; 256];
18    let mut value = 0u8;
19    while value < 64 {
20        let ch = match value {
21            0..=25 => b'A' + value,
22            26..=51 => b'a' + (value - 26),
23            52..=61 => b'0' + (value - 52),
24            62 => b'+',
25            _ => b'/',
26        };
27        table[ch as usize] = value;
28        value += 1;
29    }
30    table
31}
32
33enum Base64Byte {
34    Consumed,
35    Stop,
36    Invalid,
37}
38
39struct Base64Decoder {
40    chunk: u32,
41    byte_count: u8,
42    buf: Vec<u8>,
43}
44
45impl Base64Decoder {
46    fn with_capacity(capacity: usize) -> Self {
47        Base64Decoder {
48            chunk: 0,
49            byte_count: 0,
50            buf: Vec::with_capacity(capacity),
51        }
52    }
53
54    #[inline(always)]
55    fn push(&mut self, ch: u8, stop_char: u8) -> Base64Byte {
56        let val = BASE64_DECODE[ch as usize];
57        if val < 0x40 {
58            self.chunk = (self.chunk << 6) | val as u32;
59            self.byte_count = (self.byte_count + 1) & 3;
60            if self.byte_count == 0 {
61                self.buf.extend_from_slice(&self.chunk.to_be_bytes()[1..]);
62            }
63            Base64Byte::Consumed
64        } else if ch == b'=' {
65            self.flush();
66            Base64Byte::Consumed
67        } else if matches!(ch, b' ' | b'\t' | b'\r' | b'\n') {
68            Base64Byte::Consumed
69        } else if ch == stop_char {
70            Base64Byte::Stop
71        } else {
72            Base64Byte::Invalid
73        }
74    }
75
76    #[inline(always)]
77    fn decode_groups<'a>(&mut self, mut rest: &'a [u8]) -> &'a [u8] {
78        if self.byte_count != 0 {
79            return rest;
80        }
81        while let [a, b, c, d, tail @ ..] = rest {
82            let a = BASE64_DECODE[*a as usize];
83            let b = BASE64_DECODE[*b as usize];
84            let c = BASE64_DECODE[*c as usize];
85            let d = BASE64_DECODE[*d as usize];
86            if a | b | c | d >= 0x40 {
87                break;
88            }
89            let word = ((a as u32) << 18) | ((b as u32) << 12) | ((c as u32) << 6) | d as u32;
90            self.buf.extend_from_slice(&word.to_be_bytes()[1..]);
91            rest = tail;
92        }
93        rest
94    }
95
96    #[inline(always)]
97    fn flush(&mut self) {
98        match self.byte_count {
99            2 => self.buf.push((self.chunk >> 4) as u8),
100            3 => self
101                .buf
102                .extend_from_slice(&[(self.chunk >> 10) as u8, (self.chunk >> 2) as u8]),
103            _ => (),
104        }
105        self.byte_count = 0;
106    }
107
108    fn finish(mut self) -> Vec<u8> {
109        self.flush();
110        self.buf
111    }
112}
113
114#[cfg(feature = "base64_slice")]
115#[inline(always)]
116fn base64_capacity(bytes: &[u8], stop_char: u8) -> usize {
117    let len = if BASE64_DECODE[stop_char as usize] == B64_INVALID
118        && stop_char != b'='
119        && !matches!(stop_char, b' ' | b'\t' | b'\r' | b'\n')
120    {
121        let window = bytes.len().min(B64_STOP_SCAN);
122        memchr::memchr(stop_char, bytes.get(..window).unwrap_or(bytes)).unwrap_or(bytes.len())
123    } else {
124        bytes.len()
125    };
126    len.div_ceil(4) * 3
127}
128
129#[inline(always)]
130pub fn base64_decode(bytes: &[u8]) -> Option<Vec<u8>> {
131    decode_slice(bytes, u8::MAX, bytes.len().div_ceil(4) * 3).map(|(decoded, _)| decoded)
132}
133
134#[cfg(feature = "base64_slice")]
135pub fn base64_decode_slice(bytes: &[u8], stop_char: u8) -> Option<(Vec<u8>, usize)> {
136    decode_slice(bytes, stop_char, base64_capacity(bytes, stop_char))
137}
138
139fn decode_slice(bytes: &[u8], stop_char: u8, capacity: usize) -> Option<(Vec<u8>, usize)> {
140    let mut decoder = Base64Decoder::with_capacity(capacity);
141    let mut rest = bytes;
142    loop {
143        rest = decoder.decode_groups(rest);
144        let Some((&ch, tail)) = rest.split_first() else {
145            break;
146        };
147        rest = tail;
148        match decoder.push(ch, stop_char) {
149            Base64Byte::Consumed => (),
150            Base64Byte::Stop => return Some((decoder.finish(), bytes.len() - rest.len())),
151            Base64Byte::Invalid => return None,
152        }
153    }
154    Some((decoder.finish(), bytes.len()))
155}
156
157pub fn base64_decode_stream<'x>(
158    stream: impl Iterator<Item = &'x u8>,
159    stream_len: usize,
160    stop_char: u8,
161) -> Option<Vec<u8>> {
162    let mut decoder = Base64Decoder::with_capacity(stream_len / 4 * 3);
163    for &ch in stream {
164        match decoder.push(ch, stop_char) {
165            Base64Byte::Consumed => (),
166            Base64Byte::Stop => return Some(decoder.finish()),
167            Base64Byte::Invalid => return None,
168        }
169    }
170    Some(decoder.finish())
171}
172
173impl<'x> MessageStream<'x> {
174    pub fn decode_base64_mime(&mut self, boundary: &[u8]) -> (usize, Cow<'x, [u8]>) {
175        let mut chunk: u32 = 0;
176        let mut byte_count: u8 = 0;
177
178        let mut buf = Vec::with_capacity(self.remaining() / 4 * 3);
179        let mut last_ch = b'\n';
180        let mut before_last_ch = 0;
181        let mut end_pos = self.offset();
182
183        self.checkpoint();
184
185        while let Some(&ch) = self.next() {
186            let val = BASE64_MAP[byte_count as usize][ch as usize];
187
188            if val < 0x01ffffff {
189                byte_count = (byte_count + 1) & 3;
190
191                if byte_count == 1 {
192                    chunk = val;
193                } else {
194                    chunk |= val;
195
196                    if byte_count == 0 {
197                        buf.extend_from_slice(&chunk.to_le_bytes()[0..3]);
198                    }
199                }
200            } else {
201                match ch {
202                    b'=' => match byte_count {
203                        1 | 2 => {
204                            buf.push(chunk.to_le_bytes()[0]);
205                            byte_count = 0;
206                        }
207                        3 => {
208                            buf.extend_from_slice(&chunk.to_le_bytes()[0..2]);
209                            byte_count = 0;
210                        }
211                        0 => (),
212                        _ => {
213                            self.restore();
214                            return (usize::MAX, b""[..].into());
215                        }
216                    },
217                    b'\n' => {
218                        end_pos = if last_ch == b'\r' {
219                            self.offset() - 2
220                        } else {
221                            self.offset() - 1
222                        }
223                    }
224                    b' ' | b'\t' | b'\r' => (),
225                    b'-' => {
226                        if last_ch == b'-' {
227                            return if !boundary.is_empty() && self.try_skip(boundary) {
228                                buf.shrink_to_fit();
229                                (
230                                    if before_last_ch == b'\n' {
231                                        end_pos
232                                    } else {
233                                        self.offset() - boundary.len() - 2
234                                    },
235                                    buf.into(),
236                                )
237                            } else {
238                                self.restore();
239                                (usize::MAX, b""[..].into())
240                            };
241                        }
242                    }
243                    _ => {
244                        self.restore();
245                        return (usize::MAX, b""[..].into());
246                    }
247                }
248            }
249
250            before_last_ch = last_ch;
251            last_ch = ch;
252        }
253
254        buf.shrink_to_fit();
255        (
256            if boundary.is_empty() {
257                self.offset()
258            } else {
259                self.restore();
260                usize::MAX
261            },
262            buf.into(),
263        )
264    }
265
266    pub fn decode_base64_word(&mut self) -> Option<Vec<u8>> {
267        let mut chunk: u32 = 0;
268        let mut byte_count: u8 = 0;
269        let mut buf = Vec::with_capacity(64);
270
271        while let Some(&ch) = self.next() {
272            match ch {
273                b'=' => {
274                    match byte_count {
275                        1 | 2 => {
276                            buf.push(chunk.to_le_bytes()[0]);
277                            byte_count = 0;
278                        }
279                        3 => {
280                            buf.extend_from_slice(&chunk.to_le_bytes()[0..2]);
281                            byte_count = 0;
282                        }
283                        0 => (),
284                        _ => {
285                            // Invalid
286                            break;
287                        }
288                    }
289                }
290                b'?' => {
291                    if let Some(b'=') = self.next() {
292                        return Some(buf);
293                    } else {
294                        break;
295                    }
296                }
297                b'\n' => {
298                    if !self.next_is_space() {
299                        break;
300                    }
301                }
302                b' ' | b'\t' | b'\r' => (),
303                _ => {
304                    let val = BASE64_MAP[byte_count as usize][ch as usize];
305
306                    if val < 0x01ffffff {
307                        byte_count = (byte_count + 1) & 3;
308
309                        if byte_count == 1 {
310                            chunk = val;
311                        } else {
312                            chunk |= val;
313
314                            if byte_count == 0 {
315                                buf.extend_from_slice(&chunk.to_le_bytes()[0..3]);
316                            }
317                        }
318                    } else {
319                        break;
320                    }
321                }
322            }
323        }
324
325        None
326    }
327}
328
329/*
330 * Table adapted from Nick Galbreath's "High performance base64 encoder / decoder"
331 *
332 * Copyright 2005, 2006, 2007 Nick Galbreath -- nickg [at] modp [dot] com
333 * All rights reserved.
334 *
335 * http://code.google.com/p/stringencoders/
336 *
337 * Released under bsd license.
338 *
339 */
340
341pub static BASE64_MAP: &[&[u32]] = &[
342    &[
343        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
344        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
345        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
346        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
347        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
348        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
349        0x01ffffff, 0x000000f8, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000fc, 0x000000d0,
350        0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4, 0x000000e8, 0x000000ec,
351        0x000000f0, 0x000000f4, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
352        0x01ffffff, 0x01ffffff, 0x00000000, 0x00000004, 0x00000008, 0x0000000c, 0x00000010,
353        0x00000014, 0x00000018, 0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c,
354        0x00000030, 0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048,
355        0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060, 0x00000064,
356        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000068,
357        0x0000006c, 0x00000070, 0x00000074, 0x00000078, 0x0000007c, 0x00000080, 0x00000084,
358        0x00000088, 0x0000008c, 0x00000090, 0x00000094, 0x00000098, 0x0000009c, 0x000000a0,
359        0x000000a4, 0x000000a8, 0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc,
360        0x000000c0, 0x000000c4, 0x000000c8, 0x000000cc, 0x01ffffff, 0x01ffffff, 0x01ffffff,
361        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
362        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
363        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
364        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
365        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
366        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
367        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
368        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
369        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
370        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
371        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
372        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
373        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
374        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
375        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
376        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
377        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
378        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
379        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
380    ],
381    &[
382        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
383        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
384        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
385        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
386        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
387        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
388        0x01ffffff, 0x0000e003, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000f003, 0x00004003,
389        0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003, 0x0000a003, 0x0000b003,
390        0x0000c003, 0x0000d003, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
391        0x01ffffff, 0x01ffffff, 0x00000000, 0x00001000, 0x00002000, 0x00003000, 0x00004000,
392        0x00005000, 0x00006000, 0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000,
393        0x0000c000, 0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001,
394        0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001, 0x00009001,
395        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000a001,
396        0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001, 0x0000f001, 0x00000002, 0x00001002,
397        0x00002002, 0x00003002, 0x00004002, 0x00005002, 0x00006002, 0x00007002, 0x00008002,
398        0x00009002, 0x0000a002, 0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002,
399        0x00000003, 0x00001003, 0x00002003, 0x00003003, 0x01ffffff, 0x01ffffff, 0x01ffffff,
400        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
401        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
402        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
403        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
404        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
405        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
406        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
407        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
408        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
409        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
410        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
411        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
412        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
413        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
414        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
415        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
416        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
417        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
418        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
419    ],
420    &[
421        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
422        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
423        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
424        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
425        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
426        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
427        0x01ffffff, 0x00800f00, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00c00f00, 0x00000d00,
428        0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00, 0x00800e00, 0x00c00e00,
429        0x00000f00, 0x00400f00, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
430        0x01ffffff, 0x01ffffff, 0x00000000, 0x00400000, 0x00800000, 0x00c00000, 0x00000100,
431        0x00400100, 0x00800100, 0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200,
432        0x00000300, 0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400,
433        0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600, 0x00400600,
434        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00800600,
435        0x00c00600, 0x00000700, 0x00400700, 0x00800700, 0x00c00700, 0x00000800, 0x00400800,
436        0x00800800, 0x00c00800, 0x00000900, 0x00400900, 0x00800900, 0x00c00900, 0x00000a00,
437        0x00400a00, 0x00800a00, 0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00,
438        0x00000c00, 0x00400c00, 0x00800c00, 0x00c00c00, 0x01ffffff, 0x01ffffff, 0x01ffffff,
439        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
440        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
441        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
442        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
443        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
444        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
445        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
446        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
447        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
448        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
449        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
450        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
451        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
452        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
453        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
454        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
455        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
456        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
457        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
458    ],
459    &[
460        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
461        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
462        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
463        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
464        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
465        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
466        0x01ffffff, 0x003e0000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003f0000, 0x00340000,
467        0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000, 0x003a0000, 0x003b0000,
468        0x003c0000, 0x003d0000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
469        0x01ffffff, 0x01ffffff, 0x00000000, 0x00010000, 0x00020000, 0x00030000, 0x00040000,
470        0x00050000, 0x00060000, 0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000,
471        0x000c0000, 0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000,
472        0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000, 0x00190000,
473        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x001a0000,
474        0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000, 0x001f0000, 0x00200000, 0x00210000,
475        0x00220000, 0x00230000, 0x00240000, 0x00250000, 0x00260000, 0x00270000, 0x00280000,
476        0x00290000, 0x002a0000, 0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000,
477        0x00300000, 0x00310000, 0x00320000, 0x00330000, 0x01ffffff, 0x01ffffff, 0x01ffffff,
478        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
479        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
480        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
481        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
482        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
483        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
484        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
485        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
486        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
487        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
488        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
489        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
490        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
491        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
492        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
493        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
494        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
495        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
496        0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
497    ],
498];
499
500#[cfg(test)]
501mod tests {
502    use crate::parsers::MessageStream;
503
504    #[test]
505    fn decode_base64() {
506        for (encoded_str, expected_result) in [
507            ("VGVzdA==", "Test"),
508            ("WWU=", "Ye"),
509            ("QQ==", "A"),
510            ("cm8=", "ro"),
511            (
512                "QXJlIHlvdSBhIFNoaW1hbm8gb3IgQ2FtcGFnbm9sbyBwZXJzb24/",
513                "Are you a Shimano or Campagnolo person?",
514            ),
515            (
516                "PCFET0NUWVBFIGh0bWw+CjxodG1sPgo8Ym9keT4KPC9ib2R5Pgo8L2h0bWw+Cg==",
517                "<!DOCTYPE html>\n<html>\n<body>\n</body>\n</html>\n",
518            ),
519            (
520                "PCFET0NUWVBFIGh0bWw+CjxodG1sPg\no8Ym9ke\nT4KPC 9ib2R5Pg\n o8L2h0bWw+Cg==",
521                "<!DOCTYPE html>\n<html>\n<body>\n</body>\n</html>\n",
522            ),
523            ("w6HDqcOtw7PDug==", "áéíóú"),
524            ("====", ""),
525            ("w6HDq!cOtw7PDug=", ""),
526            ("w6 HD qcOt", "áéí"),
527            ("cmáé", ""),
528            ("áé", ""),
529            ("w\n6\nH\nD\nq\nc\nO\nt\nw\n7\n P\tD u g\n==", "áéíóú"),
530            ("w6HDqcOtw7PDug==", "áéíóú"),
531        ] {
532            assert_eq!(
533                super::base64_decode(encoded_str.as_bytes()).unwrap_or_default(),
534                expected_result.as_bytes(),
535                "Failed for {encoded_str:?}",
536            );
537        }
538    }
539
540    #[test]
541    fn decode_base64_mime() {
542        for (encoded_str, expected_result) in [
543            ("VGVzdA==\r\n--boundary\n", "Test"),
544            (
545                "PCFET0NUWVBFIGh0bWw+CjxodG1sPg\no8Ym9ke\nT4KPC 9ib2R5Pg\n o8L2h0bWw+Cg==\r\n--boundary--\r\n",
546                "<!DOCTYPE html>\n<html>\n<body>\n</body>\n</html>\n",
547            ),
548            ("w6HDqcOtw7PDug==\r\n--boundary \n", "áéíóú"),
549            (
550                "w\n6\nH\nD\nq\nc\nO\nt\nw\n7\n P\tD u g\n==\r\n--boundary\n",
551                "áéíóú",
552            ),
553            ("w6HDqcOtw7PDug==--boundary", "áéíóú"),
554            ("w6HDqcOtw7PDug==\n--boundary--", "áéíóú"),
555            (
556                "w\n6\nH\nD\nq\nc\nO\nt\nw\n7\n P\tD u g\n==\n--boundary",
557                "áéíóú",
558            ),
559        ] {
560            let mut s = MessageStream::new(encoded_str.as_bytes());
561            let (_, result) = s.decode_base64_mime(b"boundary");
562
563            assert_eq!(
564                result,
565                expected_result.as_bytes(),
566                "Failed for {encoded_str:?}",
567            );
568        }
569    }
570
571    #[test]
572    fn decode_base64_word() {
573        for (encoded_str, expected_result) in [
574            ("w 6 H D q c O t w 7 P D u g==  ?=", "áéíóú"),
575            ("w6HDqcOtw7PDug==?=", "áéíóú"),
576            ("w6HDqc\n  Otw7PDug==?=", "áéíóú"),
577            ("w6HDqcOtw7PDug================?=", "áéíóú"),
578            ("?=", ""),
579        ] {
580            let mut s = MessageStream::new(encoded_str.as_bytes());
581            assert_eq!(
582                s.decode_base64_word().unwrap(),
583                expected_result.as_bytes(),
584                "Failed for {encoded_str:?}",
585            );
586        }
587    }
588}