e_utils/algorithm/base64/
decode.rs1use super::{tables, PAD_BYTE};
2use super::{Config, STANDARD};
3use std::fmt;
4
5pub fn decode<T: AsRef<[u8]>>(input: T) -> Result<Vec<u8>, DecodeError> {
7 decode_config(input, STANDARD)
8}
9pub fn decode_config<T: AsRef<[u8]>>(input: T, config: Config) -> Result<Vec<u8>, DecodeError> {
11 let mut buffer = Vec::<u8>::with_capacity(input.as_ref().len() * 4 / 3);
12 decode_config_buf(input, config, &mut buffer).map(|_| buffer)
13}
14
15#[derive(Clone, Debug, PartialEq, Eq)]
17pub enum DecodeError {
18 InvalidByte(usize, u8),
20 InvalidLength,
26 InvalidLastSymbol(usize, u8),
31}
32
33impl fmt::Display for DecodeError {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 match *self {
36 DecodeError::InvalidByte(index, byte) => {
37 write!(f, "Invalid byte {}, offset {}.", byte, index)
38 }
39 DecodeError::InvalidLength => write!(f, "Encoded text cannot have a 6-bit remainder."),
40 DecodeError::InvalidLastSymbol(index, byte) => {
41 write!(f, "Invalid last symbol {}, offset {}.", byte, index)
42 }
43 }
44 }
45}
46pub fn decode_config_buf<T: AsRef<[u8]>>(
48 input: T,
49 config: Config,
50 buffer: &mut Vec<u8>,
51) -> Result<(), DecodeError> {
52 let input_bytes = input.as_ref();
53
54 let starting_output_len = buffer.len();
55
56 let num_chunks = num_chunks(input_bytes);
57 let decoded_len_estimate = num_chunks
58 .checked_mul(DECODED_CHUNK_LEN)
59 .and_then(|p| p.checked_add(starting_output_len))
60 .expect("Overflow when calculating output buffer length");
61 buffer.resize(decoded_len_estimate, 0);
62
63 let bytes_written;
64 {
65 let buffer_slice = &mut buffer.as_mut_slice()[starting_output_len..];
66 bytes_written = decode_helper(input_bytes, num_chunks, config, buffer_slice)?;
67 }
68
69 buffer.truncate(starting_output_len + bytes_written);
70
71 Ok(())
72}
73const INPUT_CHUNK_LEN: usize = 8;
75const DECODED_CHUNK_LEN: usize = 6;
76fn num_chunks(input: &[u8]) -> usize {
78 input
79 .len()
80 .checked_add(INPUT_CHUNK_LEN - 1)
81 .expect("Overflow when calculating number of chunks in input")
82 / INPUT_CHUNK_LEN
83}
84const DECODED_CHUNK_SUFFIX: usize = 2;
88const CHUNKS_PER_FAST_LOOP_BLOCK: usize = 4;
90const INPUT_BLOCK_LEN: usize = CHUNKS_PER_FAST_LOOP_BLOCK * INPUT_CHUNK_LEN;
91const DECODED_BLOCK_LEN: usize =
93 CHUNKS_PER_FAST_LOOP_BLOCK * DECODED_CHUNK_LEN + DECODED_CHUNK_SUFFIX;
94
95#[inline]
101fn decode_helper(
102 input: &[u8],
103 num_chunks: usize,
104 config: Config,
105 output: &mut [u8],
106) -> Result<usize, DecodeError> {
107 let char_set = config.char_set;
108 let decode_table = char_set.decode_table();
109
110 let remainder_len = input.len() % INPUT_CHUNK_LEN;
111
112 let trailing_bytes_to_skip = match remainder_len {
117 0 => INPUT_CHUNK_LEN,
120 1 | 5 => {
122 if let Some(b) = input.last() {
125 if *b != PAD_BYTE && decode_table[*b as usize] == tables::INVALID_VALUE {
126 return Err(DecodeError::InvalidByte(input.len() - 1, *b));
127 }
128 }
129
130 return Err(DecodeError::InvalidLength);
131 }
132 2 => INPUT_CHUNK_LEN + 2,
136 3 => INPUT_CHUNK_LEN + 3,
141 4 => INPUT_CHUNK_LEN + 4,
144 _ => remainder_len,
147 };
148
149 let mut remaining_chunks = num_chunks;
151
152 let mut input_index = 0;
153 let mut output_index = 0;
154
155 {
156 let length_of_fast_decode_chunks = input.len().saturating_sub(trailing_bytes_to_skip);
157
158 if let Some(max_start_index) = length_of_fast_decode_chunks.checked_sub(INPUT_BLOCK_LEN) {
161 while input_index <= max_start_index {
162 let input_slice = &input[input_index..(input_index + INPUT_BLOCK_LEN)];
163 let output_slice = &mut output[output_index..(output_index + DECODED_BLOCK_LEN)];
164
165 decode_chunk(
166 &input_slice[0..],
167 input_index,
168 decode_table,
169 &mut output_slice[0..],
170 )?;
171 decode_chunk(
172 &input_slice[8..],
173 input_index + 8,
174 decode_table,
175 &mut output_slice[6..],
176 )?;
177 decode_chunk(
178 &input_slice[16..],
179 input_index + 16,
180 decode_table,
181 &mut output_slice[12..],
182 )?;
183 decode_chunk(
184 &input_slice[24..],
185 input_index + 24,
186 decode_table,
187 &mut output_slice[18..],
188 )?;
189
190 input_index += INPUT_BLOCK_LEN;
191 output_index += DECODED_BLOCK_LEN - DECODED_CHUNK_SUFFIX;
192 remaining_chunks -= CHUNKS_PER_FAST_LOOP_BLOCK;
193 }
194 }
195
196 if let Some(max_start_index) = length_of_fast_decode_chunks.checked_sub(INPUT_CHUNK_LEN) {
199 while input_index < max_start_index {
200 decode_chunk(
201 &input[input_index..(input_index + INPUT_CHUNK_LEN)],
202 input_index,
203 decode_table,
204 &mut output[output_index..(output_index + DECODED_CHUNK_LEN + DECODED_CHUNK_SUFFIX)],
205 )?;
206
207 output_index += DECODED_CHUNK_LEN;
208 input_index += INPUT_CHUNK_LEN;
209 remaining_chunks -= 1;
210 }
211 }
212 }
213
214 for _ in 1..remaining_chunks {
222 decode_chunk_precise(
223 &input[input_index..],
224 input_index,
225 decode_table,
226 &mut output[output_index..(output_index + DECODED_CHUNK_LEN)],
227 )?;
228
229 input_index += INPUT_CHUNK_LEN;
230 output_index += DECODED_CHUNK_LEN;
231 }
232
233 debug_assert!(input.len() - input_index > 1 || input.is_empty());
235 debug_assert!(input.len() - input_index <= 8);
236
237 let mut leftover_bits: u64 = 0;
241 let mut morsels_in_leftover = 0;
242 let mut padding_bytes = 0;
243 let mut first_padding_index: usize = 0;
244 let mut last_symbol = 0_u8;
245 let start_of_leftovers = input_index;
246 for (i, b) in input[start_of_leftovers..].iter().enumerate() {
247 if *b == PAD_BYTE {
249 if i % 4 < 2 {
259 let bad_padding_index = start_of_leftovers
261 + if padding_bytes > 0 {
262 first_padding_index
267 } else {
268 i
270 };
271 return Err(DecodeError::InvalidByte(bad_padding_index, *b));
272 }
273
274 if padding_bytes == 0 {
275 first_padding_index = i;
276 }
277
278 padding_bytes += 1;
279 continue;
280 }
281
282 if padding_bytes > 0 {
287 return Err(DecodeError::InvalidByte(
288 start_of_leftovers + first_padding_index,
289 PAD_BYTE,
290 ));
291 }
292 last_symbol = *b;
293
294 let shift = 64 - (morsels_in_leftover + 1) * 6;
297 let morsel = decode_table[*b as usize];
299 if morsel == tables::INVALID_VALUE {
300 return Err(DecodeError::InvalidByte(start_of_leftovers + i, *b));
301 }
302
303 leftover_bits |= (morsel as u64) << shift;
304 morsels_in_leftover += 1;
305 }
306
307 let leftover_bits_ready_to_append = match morsels_in_leftover {
308 0 => 0,
309 2 => 8,
310 3 => 16,
311 4 => 24,
312 6 => 32,
313 7 => 40,
314 8 => 48,
315 _ => unreachable!(
316 "Impossible: must only have 0 to 8 input bytes in last chunk, with no invalid lengths"
317 ),
318 };
319
320 let mask = !0 >> leftover_bits_ready_to_append;
323 if !config.decode_allow_trailing_bits && (leftover_bits & mask) != 0 {
324 return Err(DecodeError::InvalidLastSymbol(
326 start_of_leftovers + morsels_in_leftover - 1,
327 last_symbol,
328 ));
329 }
330
331 let mut leftover_bits_appended_to_buf = 0;
332 while leftover_bits_appended_to_buf < leftover_bits_ready_to_append {
333 let selected_bits = (leftover_bits >> (56 - leftover_bits_appended_to_buf)) as u8;
335 output[output_index] = selected_bits;
336 output_index += 1;
337
338 leftover_bits_appended_to_buf += 8;
339 }
340
341 Ok(output_index)
342}
343
344#[inline(always)]
355fn decode_chunk(
356 input: &[u8],
357 index_at_start_of_input: usize,
358 decode_table: &[u8; 256],
359 output: &mut [u8],
360) -> Result<(), DecodeError> {
361 let mut accum: u64;
362
363 let morsel = decode_table[input[0] as usize];
364 if morsel == tables::INVALID_VALUE {
365 return Err(DecodeError::InvalidByte(index_at_start_of_input, input[0]));
366 }
367 accum = (morsel as u64) << 58;
368
369 let morsel = decode_table[input[1] as usize];
370 if morsel == tables::INVALID_VALUE {
371 return Err(DecodeError::InvalidByte(
372 index_at_start_of_input + 1,
373 input[1],
374 ));
375 }
376 accum |= (morsel as u64) << 52;
377
378 let morsel = decode_table[input[2] as usize];
379 if morsel == tables::INVALID_VALUE {
380 return Err(DecodeError::InvalidByte(
381 index_at_start_of_input + 2,
382 input[2],
383 ));
384 }
385 accum |= (morsel as u64) << 46;
386
387 let morsel = decode_table[input[3] as usize];
388 if morsel == tables::INVALID_VALUE {
389 return Err(DecodeError::InvalidByte(
390 index_at_start_of_input + 3,
391 input[3],
392 ));
393 }
394 accum |= (morsel as u64) << 40;
395
396 let morsel = decode_table[input[4] as usize];
397 if morsel == tables::INVALID_VALUE {
398 return Err(DecodeError::InvalidByte(
399 index_at_start_of_input + 4,
400 input[4],
401 ));
402 }
403 accum |= (morsel as u64) << 34;
404
405 let morsel = decode_table[input[5] as usize];
406 if morsel == tables::INVALID_VALUE {
407 return Err(DecodeError::InvalidByte(
408 index_at_start_of_input + 5,
409 input[5],
410 ));
411 }
412 accum |= (morsel as u64) << 28;
413
414 let morsel = decode_table[input[6] as usize];
415 if morsel == tables::INVALID_VALUE {
416 return Err(DecodeError::InvalidByte(
417 index_at_start_of_input + 6,
418 input[6],
419 ));
420 }
421 accum |= (morsel as u64) << 22;
422
423 let morsel = decode_table[input[7] as usize];
424 if morsel == tables::INVALID_VALUE {
425 return Err(DecodeError::InvalidByte(
426 index_at_start_of_input + 7,
427 input[7],
428 ));
429 }
430 accum |= (morsel as u64) << 16;
431
432 write_u64(output, accum);
433
434 Ok(())
435}
436
437#[inline]
438fn write_u64(output: &mut [u8], value: u64) {
439 output[..8].copy_from_slice(&value.to_be_bytes());
440}
441
442#[inline]
445fn decode_chunk_precise(
446 input: &[u8],
447 index_at_start_of_input: usize,
448 decode_table: &[u8; 256],
449 output: &mut [u8],
450) -> Result<(), DecodeError> {
451 let mut tmp_buf = [0_u8; 8];
452
453 decode_chunk(
454 input,
455 index_at_start_of_input,
456 decode_table,
457 &mut tmp_buf[..],
458 )?;
459
460 output[0..6].copy_from_slice(&tmp_buf[0..6]);
461
462 Ok(())
463}