1 use crate::Config;
2 #[cfg(any(feature = "alloc", feature = "std", test))]
3 use crate::{chunked_encoder, STANDARD};
4 #[cfg(any(feature = "alloc", feature = "std", test))]
5 use alloc::{string::String, vec};
6 use core::convert::TryInto;
7 
8 ///Encode arbitrary octets as base64.
9 ///Returns a String.
10 ///Convenience for `encode_config(input, base64::STANDARD);`.
11 ///
12 ///# Example
13 ///
14 ///```rust
15 ///extern crate base64;
16 ///
17 ///fn main() {
18 ///    let b64 = base64::encode(b"hello world");
19 ///    println!("{}", b64);
20 ///}
21 ///```
22 #[cfg(any(feature = "alloc", feature = "std", test))]
encode<T: AsRef<[u8]>>(input: T) -> String23 pub fn encode<T: AsRef<[u8]>>(input: T) -> String {
24     encode_config(input, STANDARD)
25 }
26 
27 ///Encode arbitrary octets as base64.
28 ///Returns a String.
29 ///
30 ///# Example
31 ///
32 ///```rust
33 ///extern crate base64;
34 ///
35 ///fn main() {
36 ///    let b64 = base64::encode_config(b"hello world~", base64::STANDARD);
37 ///    println!("{}", b64);
38 ///
39 ///    let b64_url = base64::encode_config(b"hello internet~", base64::URL_SAFE);
40 ///    println!("{}", b64_url);
41 ///}
42 ///```
43 #[cfg(any(feature = "alloc", feature = "std", test))]
encode_config<T: AsRef<[u8]>>(input: T, config: Config) -> String44 pub 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 ///Encode arbitrary octets as base64.
56 ///Writes into the supplied output buffer, which will grow the buffer if needed.
57 ///
58 ///# Example
59 ///
60 ///```rust
61 ///extern crate base64;
62 ///
63 ///fn main() {
64 ///    let mut buf = String::new();
65 ///    base64::encode_config_buf(b"hello world~", base64::STANDARD, &mut buf);
66 ///    println!("{}", buf);
67 ///
68 ///    buf.clear();
69 ///    base64::encode_config_buf(b"hello internet~", base64::URL_SAFE, &mut buf);
70 ///    println!("{}", buf);
71 ///}
72 ///```
73 #[cfg(any(feature = "alloc", feature = "std", test))]
encode_config_buf<T: AsRef<[u8]>>(input: T, config: Config, buf: &mut String)74 pub 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 
87 /// Encode arbitrary octets as base64.
88 /// Writes into the supplied output buffer.
89 ///
90 /// This is useful if you wish to avoid allocation entirely (e.g. encoding into a stack-resident
91 /// or statically-allocated buffer).
92 ///
93 /// # Panics
94 ///
95 /// If `output` is too small to hold the encoded version of `input`, a panic will result.
96 ///
97 /// # Example
98 ///
99 /// ```rust
100 /// extern crate base64;
101 ///
102 /// fn main() {
103 ///     let s = b"hello internet!";
104 ///     let mut buf = Vec::new();
105 ///     // make sure we'll have a slice big enough for base64 + padding
106 ///     buf.resize(s.len() * 4 / 3 + 4, 0);
107 ///
108 ///     let bytes_written = base64::encode_config_slice(s,
109 ///                             base64::STANDARD, &mut buf);
110 ///
111 ///     // shorten our vec down to just what was written
112 ///     buf.resize(bytes_written, 0);
113 ///
114 ///     assert_eq!(s, base64::decode(&buf).unwrap().as_slice());
115 /// }
116 /// ```
encode_config_slice<T: AsRef<[u8]>>(input: T, config: Config, output: &mut [u8]) -> usize117 pub 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 
130 /// B64-encode and pad (if configured).
131 ///
132 /// This helper exists to avoid recalculating encoded_size, which is relatively expensive on short
133 /// inputs.
134 ///
135 /// `encoded_size` is the encoded size calculated for `input`.
136 ///
137 /// `output` must be of size `encoded_size`.
138 ///
139 /// All bytes in `output` will be written to since it is exactly the size of the output.
encode_with_padding(input: &[u8], config: Config, encoded_size: usize, output: &mut [u8])140 fn 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]
read_u64(s: &[u8]) -> u64159 fn read_u64(s: &[u8]) -> u64 {
160     u64::from_be_bytes(s[..8].try_into().unwrap())
161 }
162 
163 /// Encode input bytes to utf8 base64 bytes. Does not pad.
164 /// `output` must be long enough to hold the encoded `input` without padding.
165 /// Returns the number of bytes written.
166 #[inline]
encode_to_slice(input: &[u8], output: &mut [u8], encode_table: &[u8; 64]) -> usize167 pub 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     // we read 8 bytes at a time (u64) but only actually consume 6 of those bytes. Thus, we need
174     // 2 trailing bytes to be available to read..
175     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             // Major performance wins from letting the optimizer do the bounds check once, mostly
181             // on the output side
182             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             // Hand-unrolling for 32 vs 16 or 8 bytes produces yields performance about equivalent
186             // to unsafe pointer code on a Xeon E5-1650v3. 64 byte unrolling was slightly better for
187             // large inputs but significantly worse for 50-byte input, unsurprisingly. I suspect
188             // that it's a not uncommon use case to encode smallish chunks of data (e.g. a 64-byte
189             // SHA-512 digest), so it would be nice if that fit in the unrolled loop at least once.
190             // Plus, single-digit percentage performance differences might well be quite different
191             // on different hardware.
192 
193             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     // Encode what's left after the fast loop.
243 
244     const LOW_SIX_BITS_U8: u8 = 0x3F;
245 
246     let rem = input.len() % 3;
247     let start_of_rem = input.len() - rem;
248 
249     // start at the first index not handled by fast loop, which may be 0.
250 
251     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 
284 /// calculate the base64 encoded string size, including padding if appropriate
encoded_size(bytes_len: usize, config: Config) -> Option<usize>285 pub 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 
307 /// Write padding characters.
308 /// `output` is the slice where padding should be written, of length at least 2.
309 ///
310 /// Returns the number of padding bytes written.
add_padding(input_len: usize, output: &mut [u8]) -> usize311 pub 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] = b'=';
316         bytes_written += 1;
317     }
318 
319     bytes_written
320 }
321 
322 #[cfg(test)]
323 mod 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]
encoded_size_correct_standard()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]
encoded_size_correct_no_pad()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]
encoded_size_overflow()389     fn encoded_size_overflow() {
390         assert_eq!(None, encoded_size(std::usize::MAX, STANDARD));
391     }
392 
393     #[test]
encode_config_buf_into_nonempty_buffer_doesnt_clobber_prefix()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                 // getting convenient random single-byte printable chars that aren't base64 is
422                 // annoying
423                 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             // append plain encode onto prefix
439             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]
encode_config_slice_into_nonempty_buffer_doesnt_clobber_suffix()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             // plenty of existing garbage in the encoded buffer
472             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]
encode_config_slice_fits_into_precisely_sized_slice()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]
encode_to_slice_random_valid_utf8()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             // fill up the output buffer with garbage
568             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             // make sure the part beyond bytes_written is the same garbage it was before
579             assert_eq!(orig_output_buf[bytes_written..], output[bytes_written..]);
580 
581             // make sure the encoded bytes are UTF-8
582             let _ = str::from_utf8(&output[0..bytes_written]).unwrap();
583         }
584     }
585 
586     #[test]
encode_with_padding_random_valid_utf8()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             // fill up the output buffer with garbage
608             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             // make sure the part beyond b64 is the same garbage it was before
618             assert_eq!(orig_output_buf[encoded_size..], output[encoded_size..]);
619 
620             // make sure the encoded bytes are UTF-8
621             let _ = str::from_utf8(&output[0..encoded_size]).unwrap();
622         }
623     }
624 
625     #[test]
add_padding_random_valid_utf8()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         // cover our bases for length % 3
632         for input_len in 0..10 {
633             output.clear();
634 
635             // fill output with random
636             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             // make sure the part beyond bytes_written is the same garbage it was before
645             assert_eq!(orig_output_buf[bytes_written..], output[bytes_written..]);
646 
647             // make sure the encoded bytes are UTF-8
648             let _ = str::from_utf8(&output[0..bytes_written]).unwrap();
649         }
650     }
651 
assert_encoded_length(input_len: usize, encoded_len: usize, config: Config)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]
encode_imap()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 }
676