1use crate::{Config, PAD_BYTE};
2#[cfg(any(feature = "alloc", feature = "std", test))]
3use crate::{chunked_encoder, STANDARD};
4#[cfg(any(feature = "alloc", feature = "std", test))]
5use alloc::{string::String, vec};
6use core::convert::TryInto;
7
8#[cfg(any(feature = "alloc", feature = "std", test))]
23pub fn encode<T: AsRef<[u8]>>(input: T) -> String {
24 encode_config(input, STANDARD)
25}
26
27#[cfg(any(feature = "alloc", feature = "std", test))]
44pub fn encode_config<T: AsRef<[u8]>>(input: T, config: Config) -> String {
45 let mut buf = match encoded_size(input.as_ref().len(), config) {
46 Some(n) => vec![0; n],
47 None => panic!("integer overflow when calculating buffer size"),
48 };
49
50 encode_with_padding(input.as_ref(), config, buf.len(), &mut buf[..]);
51
52 String::from_utf8(buf).expect("Invalid UTF8")
53}
54
55#[cfg(any(feature = "alloc", feature = "std", test))]
74pub fn encode_config_buf<T: AsRef<[u8]>>(input: T, config: Config, buf: &mut String) {
75 let input_bytes = input.as_ref();
76
77 {
78 let mut sink = chunked_encoder::StringSink::new(buf);
79 let encoder = chunked_encoder::ChunkedEncoder::new(config);
80
81 encoder
82 .encode(input_bytes, &mut sink)
83 .expect("Writing to a String shouldn't fail")
84 }
85}
86
87pub fn encode_config_slice<T: AsRef<[u8]>>(input: T, config: Config, output: &mut [u8]) -> usize {
118 let input_bytes = input.as_ref();
119
120 let encoded_size = encoded_size(input_bytes.len(), config)
121 .expect("usize overflow when calculating buffer size");
122
123 let mut b64_output = &mut output[0..encoded_size];
124
125 encode_with_padding(&input_bytes, config, encoded_size, &mut b64_output);
126
127 encoded_size
128}
129
130fn encode_with_padding(input: &[u8], config: Config, encoded_size: usize, output: &mut [u8]) {
141 debug_assert_eq!(encoded_size, output.len());
142
143 let b64_bytes_written = encode_to_slice(input, output, config.char_set.encode_table());
144
145 let padding_bytes = if config.pad {
146 add_padding(input.len(), &mut output[b64_bytes_written..])
147 } else {
148 0
149 };
150
151 let encoded_bytes = b64_bytes_written
152 .checked_add(padding_bytes)
153 .expect("usize overflow when calculating b64 length");
154
155 debug_assert_eq!(encoded_size, encoded_bytes);
156}
157
158#[inline]
159fn read_u64(s: &[u8]) -> u64 {
160 u64::from_be_bytes(s[..8].try_into().unwrap())
161}
162
163#[inline]
167pub fn encode_to_slice(input: &[u8], output: &mut [u8], encode_table: &[u8; 64]) -> usize {
168 let mut input_index: usize = 0;
169
170 const BLOCKS_PER_FAST_LOOP: usize = 4;
171 const LOW_SIX_BITS: u64 = 0x3F;
172
173 let last_fast_index = input.len().saturating_sub(BLOCKS_PER_FAST_LOOP * 6 + 2);
176 let mut output_index = 0;
177
178 if last_fast_index > 0 {
179 while input_index <= last_fast_index {
180 let input_chunk = &input[input_index..(input_index + (BLOCKS_PER_FAST_LOOP * 6 + 2))];
183 let output_chunk = &mut output[output_index..(output_index + BLOCKS_PER_FAST_LOOP * 8)];
184
185 let input_u64 = read_u64(&input_chunk[0..]);
194
195 output_chunk[0] = encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize];
196 output_chunk[1] = encode_table[((input_u64 >> 52) & LOW_SIX_BITS) as usize];
197 output_chunk[2] = encode_table[((input_u64 >> 46) & LOW_SIX_BITS) as usize];
198 output_chunk[3] = encode_table[((input_u64 >> 40) & LOW_SIX_BITS) as usize];
199 output_chunk[4] = encode_table[((input_u64 >> 34) & LOW_SIX_BITS) as usize];
200 output_chunk[5] = encode_table[((input_u64 >> 28) & LOW_SIX_BITS) as usize];
201 output_chunk[6] = encode_table[((input_u64 >> 22) & LOW_SIX_BITS) as usize];
202 output_chunk[7] = encode_table[((input_u64 >> 16) & LOW_SIX_BITS) as usize];
203
204 let input_u64 = read_u64(&input_chunk[6..]);
205
206 output_chunk[8] = encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize];
207 output_chunk[9] = encode_table[((input_u64 >> 52) & LOW_SIX_BITS) as usize];
208 output_chunk[10] = encode_table[((input_u64 >> 46) & LOW_SIX_BITS) as usize];
209 output_chunk[11] = encode_table[((input_u64 >> 40) & LOW_SIX_BITS) as usize];
210 output_chunk[12] = encode_table[((input_u64 >> 34) & LOW_SIX_BITS) as usize];
211 output_chunk[13] = encode_table[((input_u64 >> 28) & LOW_SIX_BITS) as usize];
212 output_chunk[14] = encode_table[((input_u64 >> 22) & LOW_SIX_BITS) as usize];
213 output_chunk[15] = encode_table[((input_u64 >> 16) & LOW_SIX_BITS) as usize];
214
215 let input_u64 = read_u64(&input_chunk[12..]);
216
217 output_chunk[16] = encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize];
218 output_chunk[17] = encode_table[((input_u64 >> 52) & LOW_SIX_BITS) as usize];
219 output_chunk[18] = encode_table[((input_u64 >> 46) & LOW_SIX_BITS) as usize];
220 output_chunk[19] = encode_table[((input_u64 >> 40) & LOW_SIX_BITS) as usize];
221 output_chunk[20] = encode_table[((input_u64 >> 34) & LOW_SIX_BITS) as usize];
222 output_chunk[21] = encode_table[((input_u64 >> 28) & LOW_SIX_BITS) as usize];
223 output_chunk[22] = encode_table[((input_u64 >> 22) & LOW_SIX_BITS) as usize];
224 output_chunk[23] = encode_table[((input_u64 >> 16) & LOW_SIX_BITS) as usize];
225
226 let input_u64 = read_u64(&input_chunk[18..]);
227
228 output_chunk[24] = encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize];
229 output_chunk[25] = encode_table[((input_u64 >> 52) & LOW_SIX_BITS) as usize];
230 output_chunk[26] = encode_table[((input_u64 >> 46) & LOW_SIX_BITS) as usize];
231 output_chunk[27] = encode_table[((input_u64 >> 40) & LOW_SIX_BITS) as usize];
232 output_chunk[28] = encode_table[((input_u64 >> 34) & LOW_SIX_BITS) as usize];
233 output_chunk[29] = encode_table[((input_u64 >> 28) & LOW_SIX_BITS) as usize];
234 output_chunk[30] = encode_table[((input_u64 >> 22) & LOW_SIX_BITS) as usize];
235 output_chunk[31] = encode_table[((input_u64 >> 16) & LOW_SIX_BITS) as usize];
236
237 output_index += BLOCKS_PER_FAST_LOOP * 8;
238 input_index += BLOCKS_PER_FAST_LOOP * 6;
239 }
240 }
241
242 const LOW_SIX_BITS_U8: u8 = 0x3F;
245
246 let rem = input.len() % 3;
247 let start_of_rem = input.len() - rem;
248
249 while input_index < start_of_rem {
252 let input_chunk = &input[input_index..(input_index + 3)];
253 let output_chunk = &mut output[output_index..(output_index + 4)];
254
255 output_chunk[0] = encode_table[(input_chunk[0] >> 2) as usize];
256 output_chunk[1] =
257 encode_table[((input_chunk[0] << 4 | input_chunk[1] >> 4) & LOW_SIX_BITS_U8) as usize];
258 output_chunk[2] =
259 encode_table[((input_chunk[1] << 2 | input_chunk[2] >> 6) & LOW_SIX_BITS_U8) as usize];
260 output_chunk[3] = encode_table[(input_chunk[2] & LOW_SIX_BITS_U8) as usize];
261
262 input_index += 3;
263 output_index += 4;
264 }
265
266 if rem == 2 {
267 output[output_index] = encode_table[(input[start_of_rem] >> 2) as usize];
268 output[output_index + 1] = encode_table[((input[start_of_rem] << 4
269 | input[start_of_rem + 1] >> 4)
270 & LOW_SIX_BITS_U8) as usize];
271 output[output_index + 2] =
272 encode_table[((input[start_of_rem + 1] << 2) & LOW_SIX_BITS_U8) as usize];
273 output_index += 3;
274 } else if rem == 1 {
275 output[output_index] = encode_table[(input[start_of_rem] >> 2) as usize];
276 output[output_index + 1] =
277 encode_table[((input[start_of_rem] << 4) & LOW_SIX_BITS_U8) as usize];
278 output_index += 2;
279 }
280
281 output_index
282}
283
284pub fn encoded_size(bytes_len: usize, config: Config) -> Option<usize> {
286 let rem = bytes_len % 3;
287
288 let complete_input_chunks = bytes_len / 3;
289 let complete_chunk_output = complete_input_chunks.checked_mul(4);
290
291 if rem > 0 {
292 if config.pad {
293 complete_chunk_output.and_then(|c| c.checked_add(4))
294 } else {
295 let encoded_rem = match rem {
296 1 => 2,
297 2 => 3,
298 _ => unreachable!("Impossible remainder"),
299 };
300 complete_chunk_output.and_then(|c| c.checked_add(encoded_rem))
301 }
302 } else {
303 complete_chunk_output
304 }
305}
306
307pub fn add_padding(input_len: usize, output: &mut [u8]) -> usize {
312 let rem = input_len % 3;
313 let mut bytes_written = 0;
314 for _ in 0..((3 - rem) % 3) {
315 output[bytes_written] = PAD_BYTE;
316 bytes_written += 1;
317 }
318
319 bytes_written
320}
321
322#[cfg(test)]
323mod tests {
324 use super::*;
325 use crate::{
326 decode::decode_config_buf,
327 tests::{assert_encode_sanity, random_config},
328 Config, STANDARD, URL_SAFE_NO_PAD,
329 };
330
331 use rand::{
332 distributions::{Distribution, Uniform},
333 FromEntropy, Rng,
334 };
335 use std;
336 use std::str;
337
338 #[test]
339 fn encoded_size_correct_standard() {
340 assert_encoded_length(0, 0, STANDARD);
341
342 assert_encoded_length(1, 4, STANDARD);
343 assert_encoded_length(2, 4, STANDARD);
344 assert_encoded_length(3, 4, STANDARD);
345
346 assert_encoded_length(4, 8, STANDARD);
347 assert_encoded_length(5, 8, STANDARD);
348 assert_encoded_length(6, 8, STANDARD);
349
350 assert_encoded_length(7, 12, STANDARD);
351 assert_encoded_length(8, 12, STANDARD);
352 assert_encoded_length(9, 12, STANDARD);
353
354 assert_encoded_length(54, 72, STANDARD);
355
356 assert_encoded_length(55, 76, STANDARD);
357 assert_encoded_length(56, 76, STANDARD);
358 assert_encoded_length(57, 76, STANDARD);
359
360 assert_encoded_length(58, 80, STANDARD);
361 }
362
363 #[test]
364 fn encoded_size_correct_no_pad() {
365 assert_encoded_length(0, 0, URL_SAFE_NO_PAD);
366
367 assert_encoded_length(1, 2, URL_SAFE_NO_PAD);
368 assert_encoded_length(2, 3, URL_SAFE_NO_PAD);
369 assert_encoded_length(3, 4, URL_SAFE_NO_PAD);
370
371 assert_encoded_length(4, 6, URL_SAFE_NO_PAD);
372 assert_encoded_length(5, 7, URL_SAFE_NO_PAD);
373 assert_encoded_length(6, 8, URL_SAFE_NO_PAD);
374
375 assert_encoded_length(7, 10, URL_SAFE_NO_PAD);
376 assert_encoded_length(8, 11, URL_SAFE_NO_PAD);
377 assert_encoded_length(9, 12, URL_SAFE_NO_PAD);
378
379 assert_encoded_length(54, 72, URL_SAFE_NO_PAD);
380
381 assert_encoded_length(55, 74, URL_SAFE_NO_PAD);
382 assert_encoded_length(56, 75, URL_SAFE_NO_PAD);
383 assert_encoded_length(57, 76, URL_SAFE_NO_PAD);
384
385 assert_encoded_length(58, 78, URL_SAFE_NO_PAD);
386 }
387
388 #[test]
389 fn encoded_size_overflow() {
390 assert_eq!(None, encoded_size(std::usize::MAX, STANDARD));
391 }
392
393 #[test]
394 fn encode_config_buf_into_nonempty_buffer_doesnt_clobber_prefix() {
395 let mut orig_data = Vec::new();
396 let mut prefix = String::new();
397 let mut encoded_data_no_prefix = String::new();
398 let mut encoded_data_with_prefix = String::new();
399 let mut decoded = Vec::new();
400
401 let prefix_len_range = Uniform::new(0, 1000);
402 let input_len_range = Uniform::new(0, 1000);
403
404 let mut rng = rand::rngs::SmallRng::from_entropy();
405
406 for _ in 0..10_000 {
407 orig_data.clear();
408 prefix.clear();
409 encoded_data_no_prefix.clear();
410 encoded_data_with_prefix.clear();
411 decoded.clear();
412
413 let input_len = input_len_range.sample(&mut rng);
414
415 for _ in 0..input_len {
416 orig_data.push(rng.gen());
417 }
418
419 let prefix_len = prefix_len_range.sample(&mut rng);
420 for _ in 0..prefix_len {
421 prefix.push('#');
424 }
425 encoded_data_with_prefix.push_str(&prefix);
426
427 let config = random_config(&mut rng);
428 encode_config_buf(&orig_data, config, &mut encoded_data_no_prefix);
429 encode_config_buf(&orig_data, config, &mut encoded_data_with_prefix);
430
431 assert_eq!(
432 encoded_data_no_prefix.len() + prefix_len,
433 encoded_data_with_prefix.len()
434 );
435 assert_encode_sanity(&encoded_data_no_prefix, config, input_len);
436 assert_encode_sanity(&encoded_data_with_prefix[prefix_len..], config, input_len);
437
438 prefix.push_str(&mut encoded_data_no_prefix);
440
441 assert_eq!(prefix, encoded_data_with_prefix);
442
443 decode_config_buf(&encoded_data_no_prefix, config, &mut decoded).unwrap();
444 assert_eq!(orig_data, decoded);
445 }
446 }
447
448 #[test]
449 fn encode_config_slice_into_nonempty_buffer_doesnt_clobber_suffix() {
450 let mut orig_data = Vec::new();
451 let mut encoded_data = Vec::new();
452 let mut encoded_data_original_state = Vec::new();
453 let mut decoded = Vec::new();
454
455 let input_len_range = Uniform::new(0, 1000);
456
457 let mut rng = rand::rngs::SmallRng::from_entropy();
458
459 for _ in 0..10_000 {
460 orig_data.clear();
461 encoded_data.clear();
462 encoded_data_original_state.clear();
463 decoded.clear();
464
465 let input_len = input_len_range.sample(&mut rng);
466
467 for _ in 0..input_len {
468 orig_data.push(rng.gen());
469 }
470
471 for _ in 0..10 * input_len {
473 encoded_data.push(rng.gen());
474 }
475
476 encoded_data_original_state.extend_from_slice(&encoded_data);
477
478 let config = random_config(&mut rng);
479
480 let encoded_size = encoded_size(input_len, config).unwrap();
481
482 assert_eq!(
483 encoded_size,
484 encode_config_slice(&orig_data, config, &mut encoded_data)
485 );
486
487 assert_encode_sanity(
488 std::str::from_utf8(&encoded_data[0..encoded_size]).unwrap(),
489 config,
490 input_len,
491 );
492
493 assert_eq!(
494 &encoded_data[encoded_size..],
495 &encoded_data_original_state[encoded_size..]
496 );
497
498 decode_config_buf(&encoded_data[0..encoded_size], config, &mut decoded).unwrap();
499 assert_eq!(orig_data, decoded);
500 }
501 }
502
503 #[test]
504 fn encode_config_slice_fits_into_precisely_sized_slice() {
505 let mut orig_data = Vec::new();
506 let mut encoded_data = Vec::new();
507 let mut decoded = Vec::new();
508
509 let input_len_range = Uniform::new(0, 1000);
510
511 let mut rng = rand::rngs::SmallRng::from_entropy();
512
513 for _ in 0..10_000 {
514 orig_data.clear();
515 encoded_data.clear();
516 decoded.clear();
517
518 let input_len = input_len_range.sample(&mut rng);
519
520 for _ in 0..input_len {
521 orig_data.push(rng.gen());
522 }
523
524 let config = random_config(&mut rng);
525
526 let encoded_size = encoded_size(input_len, config).unwrap();
527
528 encoded_data.resize(encoded_size, 0);
529
530 assert_eq!(
531 encoded_size,
532 encode_config_slice(&orig_data, config, &mut encoded_data)
533 );
534
535 assert_encode_sanity(
536 std::str::from_utf8(&encoded_data[0..encoded_size]).unwrap(),
537 config,
538 input_len,
539 );
540
541 decode_config_buf(&encoded_data[0..encoded_size], config, &mut decoded).unwrap();
542 assert_eq!(orig_data, decoded);
543 }
544 }
545
546 #[test]
547 fn encode_to_slice_random_valid_utf8() {
548 let mut input = Vec::new();
549 let mut output = Vec::new();
550
551 let input_len_range = Uniform::new(0, 1000);
552
553 let mut rng = rand::rngs::SmallRng::from_entropy();
554
555 for _ in 0..10_000 {
556 input.clear();
557 output.clear();
558
559 let input_len = input_len_range.sample(&mut rng);
560
561 for _ in 0..input_len {
562 input.push(rng.gen());
563 }
564
565 let config = random_config(&mut rng);
566
567 let encoded_size = encoded_size(input_len, config).unwrap();
569 for _ in 0..encoded_size {
570 output.push(rng.gen());
571 }
572
573 let orig_output_buf = output.to_vec();
574
575 let bytes_written =
576 encode_to_slice(&input, &mut output, config.char_set.encode_table());
577
578 assert_eq!(orig_output_buf[bytes_written..], output[bytes_written..]);
580
581 let _ = str::from_utf8(&output[0..bytes_written]).unwrap();
583 }
584 }
585
586 #[test]
587 fn encode_with_padding_random_valid_utf8() {
588 let mut input = Vec::new();
589 let mut output = Vec::new();
590
591 let input_len_range = Uniform::new(0, 1000);
592
593 let mut rng = rand::rngs::SmallRng::from_entropy();
594
595 for _ in 0..10_000 {
596 input.clear();
597 output.clear();
598
599 let input_len = input_len_range.sample(&mut rng);
600
601 for _ in 0..input_len {
602 input.push(rng.gen());
603 }
604
605 let config = random_config(&mut rng);
606
607 let encoded_size = encoded_size(input_len, config).unwrap();
609 for _ in 0..encoded_size + 1000 {
610 output.push(rng.gen());
611 }
612
613 let orig_output_buf = output.to_vec();
614
615 encode_with_padding(&input, config, encoded_size, &mut output[0..encoded_size]);
616
617 assert_eq!(orig_output_buf[encoded_size..], output[encoded_size..]);
619
620 let _ = str::from_utf8(&output[0..encoded_size]).unwrap();
622 }
623 }
624
625 #[test]
626 fn add_padding_random_valid_utf8() {
627 let mut output = Vec::new();
628
629 let mut rng = rand::rngs::SmallRng::from_entropy();
630
631 for input_len in 0..10 {
633 output.clear();
634
635 for _ in 0..10 {
637 output.push(rng.gen());
638 }
639
640 let orig_output_buf = output.to_vec();
641
642 let bytes_written = add_padding(input_len, &mut output);
643
644 assert_eq!(orig_output_buf[bytes_written..], output[bytes_written..]);
646
647 let _ = str::from_utf8(&output[0..bytes_written]).unwrap();
649 }
650 }
651
652 fn assert_encoded_length(input_len: usize, encoded_len: usize, config: Config) {
653 assert_eq!(encoded_len, encoded_size(input_len, config).unwrap());
654
655 let mut bytes: Vec<u8> = Vec::new();
656 let mut rng = rand::rngs::SmallRng::from_entropy();
657
658 for _ in 0..input_len {
659 bytes.push(rng.gen());
660 }
661
662 let encoded = encode_config(&bytes, config);
663 assert_encode_sanity(&encoded, config, input_len);
664
665 assert_eq!(encoded_len, encoded.len());
666 }
667
668 #[test]
669 fn encode_imap() {
670 assert_eq!(
671 encode_config(b"\xFB\xFF", crate::IMAP_MUTF7),
672 encode_config(b"\xFB\xFF", crate::STANDARD_NO_PAD).replace("/", ",")
673 );
674 }
675}