simple_base64/engine/general_purpose/
mod.rs1use crate::{
3 alphabet,
4 alphabet::Alphabet,
5 engine::{Config, DecodeMetadata, DecodePaddingMode},
6 DecodeError,
7};
8use core::convert::TryInto;
9
10mod decode;
11pub(crate) mod decode_suffix;
12
13pub use decode::GeneralPurposeEstimate;
14
15pub(crate) const INVALID_VALUE: u8 = 255;
16
17pub struct GeneralPurpose {
23 encode_table: [u8; 64],
24 decode_table: [u8; 256],
25 config: GeneralPurposeConfig,
26}
27
28impl GeneralPurpose {
29 pub const fn new(alphabet: &Alphabet, config: GeneralPurposeConfig) -> Self {
34 Self {
35 encode_table: encode_table(alphabet),
36 decode_table: decode_table(alphabet),
37 config,
38 }
39 }
40}
41
42impl super::Engine for GeneralPurpose {
43 type Config = GeneralPurposeConfig;
44 type DecodeEstimate = GeneralPurposeEstimate;
45
46 fn internal_encode(&self, input: &[u8], output: &mut [u8]) -> usize {
47 let mut input_index: usize = 0;
48
49 const BLOCKS_PER_FAST_LOOP: usize = 4;
50 const LOW_SIX_BITS: u64 = 0x3F;
51
52 let last_fast_index = input.len().saturating_sub(BLOCKS_PER_FAST_LOOP * 6 + 2);
55 let mut output_index = 0;
56
57 if last_fast_index > 0 {
58 while input_index <= last_fast_index {
59 let input_chunk =
62 &input[input_index..(input_index + (BLOCKS_PER_FAST_LOOP * 6 + 2))];
63 let output_chunk =
64 &mut output[output_index..(output_index + BLOCKS_PER_FAST_LOOP * 8)];
65
66 let input_u64 = read_u64(&input_chunk[0..]);
75
76 output_chunk[0] = self.encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize];
77 output_chunk[1] = self.encode_table[((input_u64 >> 52) & LOW_SIX_BITS) as usize];
78 output_chunk[2] = self.encode_table[((input_u64 >> 46) & LOW_SIX_BITS) as usize];
79 output_chunk[3] = self.encode_table[((input_u64 >> 40) & LOW_SIX_BITS) as usize];
80 output_chunk[4] = self.encode_table[((input_u64 >> 34) & LOW_SIX_BITS) as usize];
81 output_chunk[5] = self.encode_table[((input_u64 >> 28) & LOW_SIX_BITS) as usize];
82 output_chunk[6] = self.encode_table[((input_u64 >> 22) & LOW_SIX_BITS) as usize];
83 output_chunk[7] = self.encode_table[((input_u64 >> 16) & LOW_SIX_BITS) as usize];
84
85 let input_u64 = read_u64(&input_chunk[6..]);
86
87 output_chunk[8] = self.encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize];
88 output_chunk[9] = self.encode_table[((input_u64 >> 52) & LOW_SIX_BITS) as usize];
89 output_chunk[10] = self.encode_table[((input_u64 >> 46) & LOW_SIX_BITS) as usize];
90 output_chunk[11] = self.encode_table[((input_u64 >> 40) & LOW_SIX_BITS) as usize];
91 output_chunk[12] = self.encode_table[((input_u64 >> 34) & LOW_SIX_BITS) as usize];
92 output_chunk[13] = self.encode_table[((input_u64 >> 28) & LOW_SIX_BITS) as usize];
93 output_chunk[14] = self.encode_table[((input_u64 >> 22) & LOW_SIX_BITS) as usize];
94 output_chunk[15] = self.encode_table[((input_u64 >> 16) & LOW_SIX_BITS) as usize];
95
96 let input_u64 = read_u64(&input_chunk[12..]);
97
98 output_chunk[16] = self.encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize];
99 output_chunk[17] = self.encode_table[((input_u64 >> 52) & LOW_SIX_BITS) as usize];
100 output_chunk[18] = self.encode_table[((input_u64 >> 46) & LOW_SIX_BITS) as usize];
101 output_chunk[19] = self.encode_table[((input_u64 >> 40) & LOW_SIX_BITS) as usize];
102 output_chunk[20] = self.encode_table[((input_u64 >> 34) & LOW_SIX_BITS) as usize];
103 output_chunk[21] = self.encode_table[((input_u64 >> 28) & LOW_SIX_BITS) as usize];
104 output_chunk[22] = self.encode_table[((input_u64 >> 22) & LOW_SIX_BITS) as usize];
105 output_chunk[23] = self.encode_table[((input_u64 >> 16) & LOW_SIX_BITS) as usize];
106
107 let input_u64 = read_u64(&input_chunk[18..]);
108
109 output_chunk[24] = self.encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize];
110 output_chunk[25] = self.encode_table[((input_u64 >> 52) & LOW_SIX_BITS) as usize];
111 output_chunk[26] = self.encode_table[((input_u64 >> 46) & LOW_SIX_BITS) as usize];
112 output_chunk[27] = self.encode_table[((input_u64 >> 40) & LOW_SIX_BITS) as usize];
113 output_chunk[28] = self.encode_table[((input_u64 >> 34) & LOW_SIX_BITS) as usize];
114 output_chunk[29] = self.encode_table[((input_u64 >> 28) & LOW_SIX_BITS) as usize];
115 output_chunk[30] = self.encode_table[((input_u64 >> 22) & LOW_SIX_BITS) as usize];
116 output_chunk[31] = self.encode_table[((input_u64 >> 16) & LOW_SIX_BITS) as usize];
117
118 output_index += BLOCKS_PER_FAST_LOOP * 8;
119 input_index += BLOCKS_PER_FAST_LOOP * 6;
120 }
121 }
122
123 const LOW_SIX_BITS_U8: u8 = 0x3F;
126
127 let rem = input.len() % 3;
128 let start_of_rem = input.len() - rem;
129
130 while input_index < start_of_rem {
133 let input_chunk = &input[input_index..(input_index + 3)];
134 let output_chunk = &mut output[output_index..(output_index + 4)];
135
136 output_chunk[0] = self.encode_table[(input_chunk[0] >> 2) as usize];
137 output_chunk[1] = self.encode_table
138 [((input_chunk[0] << 4 | input_chunk[1] >> 4) & LOW_SIX_BITS_U8) as usize];
139 output_chunk[2] = self.encode_table
140 [((input_chunk[1] << 2 | input_chunk[2] >> 6) & LOW_SIX_BITS_U8) as usize];
141 output_chunk[3] = self.encode_table[(input_chunk[2] & LOW_SIX_BITS_U8) as usize];
142
143 input_index += 3;
144 output_index += 4;
145 }
146
147 if rem == 2 {
148 output[output_index] = self.encode_table[(input[start_of_rem] >> 2) as usize];
149 output[output_index + 1] =
150 self.encode_table[((input[start_of_rem] << 4 | input[start_of_rem + 1] >> 4)
151 & LOW_SIX_BITS_U8) as usize];
152 output[output_index + 2] =
153 self.encode_table[((input[start_of_rem + 1] << 2) & LOW_SIX_BITS_U8) as usize];
154 output_index += 3;
155 } else if rem == 1 {
156 output[output_index] = self.encode_table[(input[start_of_rem] >> 2) as usize];
157 output[output_index + 1] =
158 self.encode_table[((input[start_of_rem] << 4) & LOW_SIX_BITS_U8) as usize];
159 output_index += 2;
160 }
161
162 output_index
163 }
164
165 fn internal_decoded_len_estimate(&self, input_len: usize) -> Self::DecodeEstimate {
166 GeneralPurposeEstimate::new(input_len)
167 }
168
169 fn internal_decode(
170 &self,
171 input: &[u8],
172 output: &mut [u8],
173 estimate: Self::DecodeEstimate,
174 ) -> Result<DecodeMetadata, DecodeError> {
175 decode::decode_helper(
176 input,
177 estimate,
178 output,
179 &self.decode_table,
180 self.config.decode_allow_trailing_bits,
181 self.config.decode_padding_mode,
182 )
183 }
184
185 fn config(&self) -> &Self::Config {
186 &self.config
187 }
188}
189
190pub(crate) const fn encode_table(alphabet: &Alphabet) -> [u8; 64] {
192 let mut encode_table = [0_u8; 64];
195 {
196 let mut index = 0;
197 while index < 64 {
198 encode_table[index] = alphabet.symbols[index];
199 index += 1;
200 }
201 }
202
203 encode_table
204}
205
206pub(crate) const fn decode_table(alphabet: &Alphabet) -> [u8; 256] {
210 let mut decode_table = [INVALID_VALUE; 256];
211
212 let mut index = 0;
215 while index < 64 {
216 decode_table[alphabet.symbols[index] as usize] = index as u8;
219 index += 1;
220 }
221
222 decode_table
223}
224
225#[inline]
226fn read_u64(s: &[u8]) -> u64 {
227 u64::from_be_bytes(s[..8].try_into().unwrap())
228}
229
230#[derive(Clone, Copy, Debug)]
243pub struct GeneralPurposeConfig {
244 encode_padding: bool,
245 decode_allow_trailing_bits: bool,
246 decode_padding_mode: DecodePaddingMode,
247}
248
249impl GeneralPurposeConfig {
250 pub const fn new() -> Self {
256 Self {
257 encode_padding: true,
259 decode_allow_trailing_bits: false,
260 decode_padding_mode: DecodePaddingMode::RequireCanonical,
261 }
262 }
263
264 pub const fn with_encode_padding(self, padding: bool) -> Self {
275 Self {
276 encode_padding: padding,
277 ..self
278 }
279 }
280
281 pub const fn with_decode_allow_trailing_bits(self, allow: bool) -> Self {
289 Self {
290 decode_allow_trailing_bits: allow,
291 ..self
292 }
293 }
294
295 pub const fn with_decode_padding_mode(self, mode: DecodePaddingMode) -> Self {
309 Self {
310 decode_padding_mode: mode,
311 ..self
312 }
313 }
314}
315
316impl Default for GeneralPurposeConfig {
317 fn default() -> Self {
319 Self::new()
320 }
321}
322
323impl Config for GeneralPurposeConfig {
324 fn encode_padding(&self) -> bool {
325 self.encode_padding
326 }
327}
328
329pub const STANDARD: GeneralPurpose = GeneralPurpose::new(&alphabet::STANDARD, PAD);
331
332pub const STANDARD_NO_PAD: GeneralPurpose = GeneralPurpose::new(&alphabet::STANDARD, NO_PAD);
334
335pub const URL_SAFE: GeneralPurpose = GeneralPurpose::new(&alphabet::URL_SAFE, PAD);
337
338pub const URL_SAFE_NO_PAD: GeneralPurpose = GeneralPurpose::new(&alphabet::URL_SAFE, NO_PAD);
340
341pub const PAD: GeneralPurposeConfig = GeneralPurposeConfig::new();
346
347pub const NO_PAD: GeneralPurposeConfig = GeneralPurposeConfig::new()
349 .with_encode_padding(false)
350 .with_decode_padding_mode(DecodePaddingMode::RequireNone);