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 unsafe { String::from_utf8_unchecked(output) }
66}
67
68#[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}