Coverage Report

Created: 2026-06-07 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/base64-0.13.1/src/encode.rs
Line
Count
Source
1
use crate::{Config, PAD_BYTE};
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))]
23
0
pub fn encode<T: AsRef<[u8]>>(input: T) -> String {
24
0
    encode_config(input, STANDARD)
25
0
}
Unexecuted instantiation: base64::encode::encode::<&[u8]>
Unexecuted instantiation: base64::encode::encode::<_>
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))]
44
0
pub fn encode_config<T: AsRef<[u8]>>(input: T, config: Config) -> String {
45
0
    let mut buf = match encoded_size(input.as_ref().len(), config) {
46
0
        Some(n) => vec![0; n],
47
0
        None => panic!("integer overflow when calculating buffer size"),
48
    };
49
50
0
    encode_with_padding(input.as_ref(), config, buf.len(), &mut buf[..]);
51
52
0
    String::from_utf8(buf).expect("Invalid UTF8")
53
0
}
Unexecuted instantiation: base64::encode::encode_config::<&[u8]>
Unexecuted instantiation: base64::encode::encode_config::<_>
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))]
74
5.45M
pub fn encode_config_buf<T: AsRef<[u8]>>(input: T, config: Config, buf: &mut String) {
75
5.45M
    let input_bytes = input.as_ref();
76
77
    {
78
5.45M
        let mut sink = chunked_encoder::StringSink::new(buf);
79
5.45M
        let encoder = chunked_encoder::ChunkedEncoder::new(config);
80
81
5.45M
        encoder
82
5.45M
            .encode(input_bytes, &mut sink)
83
5.45M
            .expect("Writing to a String shouldn't fail")
84
    }
85
5.45M
}
base64::encode::encode_config_buf::<&[u8]>
Line
Count
Source
74
5.45M
pub fn encode_config_buf<T: AsRef<[u8]>>(input: T, config: Config, buf: &mut String) {
75
5.45M
    let input_bytes = input.as_ref();
76
77
    {
78
5.45M
        let mut sink = chunked_encoder::StringSink::new(buf);
79
5.45M
        let encoder = chunked_encoder::ChunkedEncoder::new(config);
80
81
5.45M
        encoder
82
5.45M
            .encode(input_bytes, &mut sink)
83
5.45M
            .expect("Writing to a String shouldn't fail")
84
    }
85
5.45M
}
Unexecuted instantiation: base64::encode::encode_config_buf::<_>
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
/// ```
117
0
pub fn encode_config_slice<T: AsRef<[u8]>>(input: T, config: Config, output: &mut [u8]) -> usize {
118
0
    let input_bytes = input.as_ref();
119
120
0
    let encoded_size = encoded_size(input_bytes.len(), config)
121
0
        .expect("usize overflow when calculating buffer size");
122
123
0
    let mut b64_output = &mut output[0..encoded_size];
124
125
0
    encode_with_padding(&input_bytes, config, encoded_size, &mut b64_output);
126
127
0
    encoded_size
128
0
}
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.
140
0
fn encode_with_padding(input: &[u8], config: Config, encoded_size: usize, output: &mut [u8]) {
141
0
    debug_assert_eq!(encoded_size, output.len());
142
143
0
    let b64_bytes_written = encode_to_slice(input, output, config.char_set.encode_table());
144
145
0
    let padding_bytes = if config.pad {
146
0
        add_padding(input.len(), &mut output[b64_bytes_written..])
147
    } else {
148
0
        0
149
    };
150
151
0
    let encoded_bytes = b64_bytes_written
152
0
        .checked_add(padding_bytes)
153
0
        .expect("usize overflow when calculating b64 length");
154
155
0
    debug_assert_eq!(encoded_size, encoded_bytes);
156
0
}
157
158
#[inline]
159
82.1M
fn read_u64(s: &[u8]) -> u64 {
160
82.1M
    u64::from_be_bytes(s[..8].try_into().unwrap())
161
82.1M
}
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]
167
5.80M
pub fn encode_to_slice(input: &[u8], output: &mut [u8], encode_table: &[u8; 64]) -> usize {
168
5.80M
    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
5.80M
    let last_fast_index = input.len().saturating_sub(BLOCKS_PER_FAST_LOOP * 6 + 2);
176
5.80M
    let mut output_index = 0;
177
178
5.80M
    if last_fast_index > 0 {
179
26.1M
        while input_index <= last_fast_index {
180
20.5M
            // Major performance wins from letting the optimizer do the bounds check once, mostly
181
20.5M
            // on the output side
182
20.5M
            let input_chunk = &input[input_index..(input_index + (BLOCKS_PER_FAST_LOOP * 6 + 2))];
183
20.5M
            let output_chunk = &mut output[output_index..(output_index + BLOCKS_PER_FAST_LOOP * 8)];
184
20.5M
185
20.5M
            // Hand-unrolling for 32 vs 16 or 8 bytes produces yields performance about equivalent
186
20.5M
            // to unsafe pointer code on a Xeon E5-1650v3. 64 byte unrolling was slightly better for
187
20.5M
            // large inputs but significantly worse for 50-byte input, unsurprisingly. I suspect
188
20.5M
            // that it's a not uncommon use case to encode smallish chunks of data (e.g. a 64-byte
189
20.5M
            // SHA-512 digest), so it would be nice if that fit in the unrolled loop at least once.
190
20.5M
            // Plus, single-digit percentage performance differences might well be quite different
191
20.5M
            // on different hardware.
192
20.5M
193
20.5M
            let input_u64 = read_u64(&input_chunk[0..]);
194
20.5M
195
20.5M
            output_chunk[0] = encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize];
196
20.5M
            output_chunk[1] = encode_table[((input_u64 >> 52) & LOW_SIX_BITS) as usize];
197
20.5M
            output_chunk[2] = encode_table[((input_u64 >> 46) & LOW_SIX_BITS) as usize];
198
20.5M
            output_chunk[3] = encode_table[((input_u64 >> 40) & LOW_SIX_BITS) as usize];
199
20.5M
            output_chunk[4] = encode_table[((input_u64 >> 34) & LOW_SIX_BITS) as usize];
200
20.5M
            output_chunk[5] = encode_table[((input_u64 >> 28) & LOW_SIX_BITS) as usize];
201
20.5M
            output_chunk[6] = encode_table[((input_u64 >> 22) & LOW_SIX_BITS) as usize];
202
20.5M
            output_chunk[7] = encode_table[((input_u64 >> 16) & LOW_SIX_BITS) as usize];
203
20.5M
204
20.5M
            let input_u64 = read_u64(&input_chunk[6..]);
205
20.5M
206
20.5M
            output_chunk[8] = encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize];
207
20.5M
            output_chunk[9] = encode_table[((input_u64 >> 52) & LOW_SIX_BITS) as usize];
208
20.5M
            output_chunk[10] = encode_table[((input_u64 >> 46) & LOW_SIX_BITS) as usize];
209
20.5M
            output_chunk[11] = encode_table[((input_u64 >> 40) & LOW_SIX_BITS) as usize];
210
20.5M
            output_chunk[12] = encode_table[((input_u64 >> 34) & LOW_SIX_BITS) as usize];
211
20.5M
            output_chunk[13] = encode_table[((input_u64 >> 28) & LOW_SIX_BITS) as usize];
212
20.5M
            output_chunk[14] = encode_table[((input_u64 >> 22) & LOW_SIX_BITS) as usize];
213
20.5M
            output_chunk[15] = encode_table[((input_u64 >> 16) & LOW_SIX_BITS) as usize];
214
20.5M
215
20.5M
            let input_u64 = read_u64(&input_chunk[12..]);
216
20.5M
217
20.5M
            output_chunk[16] = encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize];
218
20.5M
            output_chunk[17] = encode_table[((input_u64 >> 52) & LOW_SIX_BITS) as usize];
219
20.5M
            output_chunk[18] = encode_table[((input_u64 >> 46) & LOW_SIX_BITS) as usize];
220
20.5M
            output_chunk[19] = encode_table[((input_u64 >> 40) & LOW_SIX_BITS) as usize];
221
20.5M
            output_chunk[20] = encode_table[((input_u64 >> 34) & LOW_SIX_BITS) as usize];
222
20.5M
            output_chunk[21] = encode_table[((input_u64 >> 28) & LOW_SIX_BITS) as usize];
223
20.5M
            output_chunk[22] = encode_table[((input_u64 >> 22) & LOW_SIX_BITS) as usize];
224
20.5M
            output_chunk[23] = encode_table[((input_u64 >> 16) & LOW_SIX_BITS) as usize];
225
20.5M
226
20.5M
            let input_u64 = read_u64(&input_chunk[18..]);
227
20.5M
228
20.5M
            output_chunk[24] = encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize];
229
20.5M
            output_chunk[25] = encode_table[((input_u64 >> 52) & LOW_SIX_BITS) as usize];
230
20.5M
            output_chunk[26] = encode_table[((input_u64 >> 46) & LOW_SIX_BITS) as usize];
231
20.5M
            output_chunk[27] = encode_table[((input_u64 >> 40) & LOW_SIX_BITS) as usize];
232
20.5M
            output_chunk[28] = encode_table[((input_u64 >> 34) & LOW_SIX_BITS) as usize];
233
20.5M
            output_chunk[29] = encode_table[((input_u64 >> 28) & LOW_SIX_BITS) as usize];
234
20.5M
            output_chunk[30] = encode_table[((input_u64 >> 22) & LOW_SIX_BITS) as usize];
235
20.5M
            output_chunk[31] = encode_table[((input_u64 >> 16) & LOW_SIX_BITS) as usize];
236
20.5M
237
20.5M
            output_index += BLOCKS_PER_FAST_LOOP * 8;
238
20.5M
            input_index += BLOCKS_PER_FAST_LOOP * 6;
239
20.5M
        }
240
217k
    }
241
242
    // Encode what's left after the fast loop.
243
244
    const LOW_SIX_BITS_U8: u8 = 0x3F;
245
246
5.80M
    let rem = input.len() % 3;
247
5.80M
    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
21.0M
    while input_index < start_of_rem {
252
15.2M
        let input_chunk = &input[input_index..(input_index + 3)];
253
15.2M
        let output_chunk = &mut output[output_index..(output_index + 4)];
254
15.2M
255
15.2M
        output_chunk[0] = encode_table[(input_chunk[0] >> 2) as usize];
256
15.2M
        output_chunk[1] =
257
15.2M
            encode_table[((input_chunk[0] << 4 | input_chunk[1] >> 4) & LOW_SIX_BITS_U8) as usize];
258
15.2M
        output_chunk[2] =
259
15.2M
            encode_table[((input_chunk[1] << 2 | input_chunk[2] >> 6) & LOW_SIX_BITS_U8) as usize];
260
15.2M
        output_chunk[3] = encode_table[(input_chunk[2] & LOW_SIX_BITS_U8) as usize];
261
15.2M
262
15.2M
        input_index += 3;
263
15.2M
        output_index += 4;
264
15.2M
    }
265
266
5.80M
    if rem == 2 {
267
4.83M
        output[output_index] = encode_table[(input[start_of_rem] >> 2) as usize];
268
4.83M
        output[output_index + 1] = encode_table[((input[start_of_rem] << 4
269
4.83M
            | input[start_of_rem + 1] >> 4)
270
4.83M
            & LOW_SIX_BITS_U8) as usize];
271
4.83M
        output[output_index + 2] =
272
4.83M
            encode_table[((input[start_of_rem + 1] << 2) & LOW_SIX_BITS_U8) as usize];
273
4.83M
        output_index += 3;
274
4.83M
    } else if rem == 1 {
275
317k
        output[output_index] = encode_table[(input[start_of_rem] >> 2) as usize];
276
317k
        output[output_index + 1] =
277
317k
            encode_table[((input[start_of_rem] << 4) & LOW_SIX_BITS_U8) as usize];
278
317k
        output_index += 2;
279
652k
    }
280
281
5.80M
    output_index
282
5.80M
}
283
284
/// calculate the base64 encoded string size, including padding if appropriate
285
0
pub fn encoded_size(bytes_len: usize, config: Config) -> Option<usize> {
286
0
    let rem = bytes_len % 3;
287
288
0
    let complete_input_chunks = bytes_len / 3;
289
0
    let complete_chunk_output = complete_input_chunks.checked_mul(4);
290
291
0
    if rem > 0 {
292
0
        if config.pad {
293
0
            complete_chunk_output.and_then(|c| c.checked_add(4))
294
        } else {
295
0
            let encoded_rem = match rem {
296
0
                1 => 2,
297
0
                2 => 3,
298
0
                _ => unreachable!("Impossible remainder"),
299
            };
300
0
            complete_chunk_output.and_then(|c| c.checked_add(encoded_rem))
301
        }
302
    } else {
303
0
        complete_chunk_output
304
    }
305
0
}
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.
311
5.45M
pub fn add_padding(input_len: usize, output: &mut [u8]) -> usize {
312
5.45M
    let rem = input_len % 3;
313
5.45M
    let mut bytes_written = 0;
314
5.47M
    for _ in 0..((3 - rem) % 3) {
315
5.47M
        output[bytes_written] = PAD_BYTE;
316
5.47M
        bytes_written += 1;
317
5.47M
    }
318
319
5.45M
    bytes_written
320
5.45M
}
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]
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
}