ps_base64/encoder/
mod.rs

1pub const ALPHABET: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
2
3#[inline]
4#[must_use]
5pub fn sized_encode<const S: usize>(input: &[u8]) -> [u8; S] {
6    let mut output = [b'='; S];
7    let mut index = 0;
8
9    let mut push = |byte: u8| {
10        if index < S {
11            output[index] = byte;
12            index += 1;
13        }
14    };
15
16    for chunk in input.chunks_exact(3) {
17        push(ALPHABET[(chunk[0] >> 2) as usize]);
18        push(ALPHABET[(((chunk[0] & 0x3) << 4) | (chunk[1] >> 4)) as usize]);
19        push(ALPHABET[(((chunk[1] & 0xf) << 2) | (chunk[2] >> 6)) as usize]);
20        push(ALPHABET[(chunk[2] & 0x3f) as usize]);
21    }
22
23    let remainder = input.len() % 3;
24
25    if remainder == 1 {
26        let index: usize = input.len() - 1;
27
28        push(ALPHABET[(input[index] >> 2) as usize]);
29        push(ALPHABET[((input[index] & 3) << 4) as usize]);
30    } else if remainder == 2 {
31        let index: usize = input.len() - 2;
32
33        push(ALPHABET[(input[index] >> 2) as usize]);
34        push(ALPHABET[(((input[index] & 0x3) << 4) | (input[index + 1] >> 4)) as usize]);
35        push(ALPHABET[((input[index + 1] & 0xf) << 2) as usize]);
36    }
37
38    output
39}
40
41#[must_use]
42pub fn encode(input: &[u8]) -> String {
43    let mut output = Vec::with_capacity((input.len() * 4).div_ceil(3));
44
45    for chunk in input.chunks_exact(3) {
46        output.push(ALPHABET[(chunk[0] >> 2) as usize]);
47        output.push(ALPHABET[(((chunk[0] & 0x3) << 4) | (chunk[1] >> 4)) as usize]);
48        output.push(ALPHABET[(((chunk[1] & 0xf) << 2) | (chunk[2] >> 6)) as usize]);
49        output.push(ALPHABET[(chunk[2] & 0x3f) as usize]);
50    }
51
52    let remainder = input.len() % 3;
53    let len = input.len();
54
55    if remainder == 1 {
56        output.push(ALPHABET[(input[len - 1] >> 2) as usize]);
57        output.push(ALPHABET[((input[len - 1] & 3) << 4) as usize]);
58    } else if remainder == 2 {
59        output.push(ALPHABET[(input[len - 2] >> 2) as usize]);
60        output.push(ALPHABET[(((input[len - 2] & 0x3) << 4) | (input[len - 1] >> 4)) as usize]);
61        output.push(ALPHABET[((input[len - 1] & 0xf) << 2) as usize]);
62    }
63
64    // ALPHABET does not contain invalid utf8 chars
65    unsafe { String::from_utf8_unchecked(output) }
66}
67
68/// Encodes `input` and writes directly into a [`core::fmt::Write`].
69///
70/// # Errors
71///
72/// Errors are passed from [`core::fmt::Write::write_str`].
73#[inline]
74pub fn encode_into<W>(input: &[u8], mut sink: W) -> core::fmt::Result
75where
76    W: core::fmt::Write,
77{
78    let mut push = |s: &[u8]| sink.write_str(unsafe { str::from_utf8_unchecked(s) });
79
80    for chunk in input.chunks_exact(3) {
81        let a = ALPHABET[(chunk[0] >> 2) as usize];
82        let b = ALPHABET[(((chunk[0] & 0x3) << 4) | (chunk[1] >> 4)) as usize];
83        let c = ALPHABET[(((chunk[1] & 0xf) << 2) | (chunk[2] >> 6)) as usize];
84        let d = ALPHABET[(chunk[2] & 0x3f) as usize];
85
86        push(&[a, b, c, d])?;
87    }
88
89    match input.len() % 3 {
90        1 => {
91            let c = input[input.len() - 1];
92
93            push(&[
94                ALPHABET[(c >> 2) as usize],
95                ALPHABET[((c & 3) << 4) as usize],
96            ])?;
97        }
98        2 => {
99            let (a, b) = {
100                let len = input.len();
101                (input[len - 2], input[len - 1])
102            };
103
104            push(&[
105                ALPHABET[(a >> 2) as usize],
106                ALPHABET[(((a & 0x3) << 4) | (b >> 4)) as usize],
107                ALPHABET[((b & 0xf) << 2) as usize],
108            ])?;
109        }
110        _ => {}
111    }
112
113    Ok(())
114}