base64/
encode.rs

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///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))]
23pub 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))]
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///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))]
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
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/// ```
117pub 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.
140fn 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/// 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]
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    // 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
285pub 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.
311pub 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                // 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]
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]
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            // 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]
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]
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
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}