1 use byteorder::{BigEndian, ByteOrder};
2 use {tables, CharacterSet, Config, STANDARD};
3 
4 use std::{error, fmt, str};
5 
6 // decode logic operates on chunks of 8 input bytes without padding
7 const INPUT_CHUNK_LEN: usize = 8;
8 const DECODED_CHUNK_LEN: usize = 6;
9 // we read a u64 and write a u64, but a u64 of input only yields 6 bytes of output, so the last
10 // 2 bytes of any output u64 should not be counted as written to (but must be available in a
11 // slice).
12 const DECODED_CHUNK_SUFFIX: usize = 2;
13 
14 // how many u64's of input to handle at a time
15 const CHUNKS_PER_FAST_LOOP_BLOCK: usize = 4;
16 const INPUT_BLOCK_LEN: usize = CHUNKS_PER_FAST_LOOP_BLOCK * INPUT_CHUNK_LEN;
17 // includes the trailing 2 bytes for the final u64 write
18 const DECODED_BLOCK_LEN: usize =
19     CHUNKS_PER_FAST_LOOP_BLOCK * DECODED_CHUNK_LEN + DECODED_CHUNK_SUFFIX;
20 
21 /// Errors that can occur while decoding.
22 #[derive(Clone, Debug, PartialEq, Eq)]
23 pub enum DecodeError {
24     /// An invalid byte was found in the input. The offset and offending byte are provided.
25     InvalidByte(usize, u8),
26     /// The length of the input is invalid.
27     InvalidLength,
28 }
29 
30 impl fmt::Display for DecodeError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result31     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32         match *self {
33             DecodeError::InvalidByte(index, byte) => {
34                 write!(f, "Invalid byte {}, offset {}.", byte, index)
35             }
36             DecodeError::InvalidLength => write!(f, "Encoded text cannot have a 6-bit remainder."),
37         }
38     }
39 }
40 
41 impl error::Error for DecodeError {
description(&self) -> &str42     fn description(&self) -> &str {
43         match *self {
44             DecodeError::InvalidByte(_, _) => "invalid byte",
45             DecodeError::InvalidLength => "invalid length",
46         }
47     }
48 
cause(&self) -> Option<&error::Error>49     fn cause(&self) -> Option<&error::Error> {
50         None
51     }
52 }
53 
54 ///Decode from string reference as octets.
55 ///Returns a Result containing a Vec<u8>.
56 ///Convenience `decode_config(input, base64::STANDARD);`.
57 ///
58 ///# Example
59 ///
60 ///```rust
61 ///extern crate base64;
62 ///
63 ///fn main() {
64 ///    let bytes = base64::decode("aGVsbG8gd29ybGQ=").unwrap();
65 ///    println!("{:?}", bytes);
66 ///}
67 ///```
decode<T: ?Sized + AsRef<[u8]>>(input: &T) -> Result<Vec<u8>, DecodeError>68 pub fn decode<T: ?Sized + AsRef<[u8]>>(input: &T) -> Result<Vec<u8>, DecodeError> {
69     decode_config(input, STANDARD)
70 }
71 
72 ///Decode from string reference as octets.
73 ///Returns a Result containing a Vec<u8>.
74 ///
75 ///# Example
76 ///
77 ///```rust
78 ///extern crate base64;
79 ///
80 ///fn main() {
81 ///    let bytes = base64::decode_config("aGVsbG8gd29ybGR+Cg==", base64::STANDARD).unwrap();
82 ///    println!("{:?}", bytes);
83 ///
84 ///    let bytes_url = base64::decode_config("aGVsbG8gaW50ZXJuZXR-Cg==", base64::URL_SAFE).unwrap();
85 ///    println!("{:?}", bytes_url);
86 ///}
87 ///```
decode_config<T: ?Sized + AsRef<[u8]>>( input: &T, config: Config, ) -> Result<Vec<u8>, DecodeError>88 pub fn decode_config<T: ?Sized + AsRef<[u8]>>(
89     input: &T,
90     config: Config,
91 ) -> Result<Vec<u8>, DecodeError> {
92     let mut buffer = Vec::<u8>::with_capacity(input.as_ref().len() * 4 / 3);
93 
94     decode_config_buf(input, config, &mut buffer).map(|_| buffer)
95 }
96 
97 ///Decode from string reference as octets.
98 ///Writes into the supplied buffer to avoid allocation.
99 ///Returns a Result containing an empty tuple, aka ().
100 ///
101 ///# Example
102 ///
103 ///```rust
104 ///extern crate base64;
105 ///
106 ///fn main() {
107 ///    let mut buffer = Vec::<u8>::new();
108 ///    base64::decode_config_buf("aGVsbG8gd29ybGR+Cg==", base64::STANDARD, &mut buffer).unwrap();
109 ///    println!("{:?}", buffer);
110 ///
111 ///    buffer.clear();
112 ///
113 ///    base64::decode_config_buf("aGVsbG8gaW50ZXJuZXR-Cg==", base64::URL_SAFE, &mut buffer)
114 ///        .unwrap();
115 ///    println!("{:?}", buffer);
116 ///}
117 ///```
decode_config_buf<T: ?Sized + AsRef<[u8]>>( input: &T, config: Config, buffer: &mut Vec<u8>, ) -> Result<(), DecodeError>118 pub fn decode_config_buf<T: ?Sized + AsRef<[u8]>>(
119     input: &T,
120     config: Config,
121     buffer: &mut Vec<u8>,
122 ) -> Result<(), DecodeError> {
123     let input_copy;
124     let input_bytes = if config.strip_whitespace {
125         input_copy = copy_without_whitespace(input.as_ref());
126         input_copy.as_ref()
127     } else {
128         input.as_ref()
129     };
130 
131     let starting_output_len = buffer.len();
132 
133     let num_chunks = num_chunks(input_bytes);
134     let decoded_len_estimate = num_chunks
135         .checked_mul(DECODED_CHUNK_LEN)
136         .and_then(|p| p.checked_add(starting_output_len))
137         .expect("Overflow when calculating output buffer length");
138     buffer.resize(decoded_len_estimate, 0);
139 
140     let bytes_written;
141     {
142         let buffer_slice = &mut buffer.as_mut_slice()[starting_output_len..];
143         bytes_written = decode_helper(input_bytes, num_chunks, &config.char_set, buffer_slice)?;
144     }
145 
146     buffer.truncate(starting_output_len + bytes_written);
147 
148     Ok(())
149 }
150 
151 /// Decode the input into the provided output slice.
152 ///
153 /// This will not write any bytes past exactly what is decoded (no stray garbage bytes at the end).
154 ///
155 /// If you don't know ahead of time what the decoded length should be, size your buffer with a
156 /// conservative estimate for the decoded length of an input: 3 bytes of output for every 4 bytes of
157 /// input, rounded up, or in other words `(input_len + 3) / 4 * 3`.
158 ///
159 /// If the slice is not large enough, this will panic.
decode_config_slice<T: ?Sized + AsRef<[u8]>>( input: &T, config: Config, output: &mut [u8], ) -> Result<usize, DecodeError>160 pub fn decode_config_slice<T: ?Sized + AsRef<[u8]>>(
161     input: &T,
162     config: Config,
163     output: &mut [u8],
164 ) -> Result<usize, DecodeError> {
165     let input_copy;
166     let input_bytes = if config.strip_whitespace {
167         input_copy = copy_without_whitespace(input.as_ref());
168         input_copy.as_ref()
169     } else {
170         input.as_ref()
171     };
172 
173     decode_helper(
174         input_bytes,
175         num_chunks(input_bytes),
176         &config.char_set,
177         output,
178     )
179 }
180 
181 /// Return the number of input chunks (including a possibly partial final chunk) in the input
num_chunks(input: &[u8]) -> usize182 fn num_chunks(input: &[u8]) -> usize {
183     input
184         .len()
185         .checked_add(INPUT_CHUNK_LEN - 1)
186         .expect("Overflow when calculating number of chunks in input") / INPUT_CHUNK_LEN
187 }
188 
copy_without_whitespace(input: &[u8]) -> Vec<u8>189 fn copy_without_whitespace(input: &[u8]) -> Vec<u8> {
190     let mut input_copy = Vec::<u8>::with_capacity(input.len());
191     input_copy.extend(input.iter().filter(|b| !b" \n\t\r\x0b\x0c".contains(b)));
192 
193     input_copy
194 }
195 
196 /// Helper to avoid duplicating num_chunks calculation, which is costly on short inputs.
197 /// Returns the number of bytes written, or an error.
198 // We're on the fragile edge of compiler heuristics here. If this is not inlined, slow. If this is
199 // inlined(always), a different slow. plain ol' inline makes the benchmarks happiest at the moment,
200 // but this is fragile and the best setting changes with only minor code modifications.
201 #[inline]
decode_helper( input: &[u8], num_chunks: usize, char_set: &CharacterSet, output: &mut [u8], ) -> Result<usize, DecodeError>202 fn decode_helper(
203     input: &[u8],
204     num_chunks: usize,
205     char_set: &CharacterSet,
206     output: &mut [u8],
207 ) -> Result<usize, DecodeError> {
208     let decode_table = char_set.decode_table();
209 
210     let remainder_len = input.len() % INPUT_CHUNK_LEN;
211 
212     // Because the fast decode loop writes in groups of 8 bytes (unrolled to
213     // CHUNKS_PER_FAST_LOOP_BLOCK times 8 bytes, where possible) and outputs 8 bytes at a time (of
214     // which only 6 are valid data), we need to be sure that we stop using the fast decode loop
215     // soon enough that there will always be 2 more bytes of valid data written after that loop.
216     let trailing_bytes_to_skip = match remainder_len {
217         // if input is a multiple of the chunk size, ignore the last chunk as it may have padding,
218         // and the fast decode logic cannot handle padding
219         0 => INPUT_CHUNK_LEN,
220         // 1 and 5 trailing bytes are illegal: can't decode 6 bits of input into a byte
221         1 | 5 => return Err(DecodeError::InvalidLength),
222         // This will decode to one output byte, which isn't enough to overwrite the 2 extra bytes
223         // written by the fast decode loop. So, we have to ignore both these 2 bytes and the
224         // previous chunk.
225         2 => INPUT_CHUNK_LEN + 2,
226         // If this is 3 unpadded chars, then it would actually decode to 2 bytes. However, if this
227         // is an erroneous 2 chars + 1 pad char that would decode to 1 byte, then it should fail
228         // with an error, not panic from going past the bounds of the output slice, so we let it
229         // use stage 3 + 4.
230         3 => INPUT_CHUNK_LEN + 3,
231         // This can also decode to one output byte because it may be 2 input chars + 2 padding
232         // chars, which would decode to 1 byte.
233         4 => INPUT_CHUNK_LEN + 4,
234         // Everything else is a legal decode len (given that we don't require padding), and will
235         // decode to at least 2 bytes of output.
236         _ => remainder_len,
237     };
238 
239     // rounded up to include partial chunks
240     let mut remaining_chunks = num_chunks;
241 
242     let mut input_index = 0;
243     let mut output_index = 0;
244 
245     {
246         let length_of_fast_decode_chunks = input.len().saturating_sub(trailing_bytes_to_skip);
247 
248         // Fast loop, stage 1
249         // manual unroll to CHUNKS_PER_FAST_LOOP_BLOCK of u64s to amortize slice bounds checks
250         if let Some(max_start_index) = length_of_fast_decode_chunks.checked_sub(INPUT_BLOCK_LEN) {
251             while input_index <= max_start_index {
252                 let input_slice = &input[input_index..(input_index + INPUT_BLOCK_LEN)];
253                 let output_slice = &mut output[output_index..(output_index + DECODED_BLOCK_LEN)];
254 
255                 decode_chunk(
256                     &input_slice[0..],
257                     input_index,
258                     decode_table,
259                     &mut output_slice[0..],
260                 )?;
261                 decode_chunk(
262                     &input_slice[8..],
263                     input_index + 8,
264                     decode_table,
265                     &mut output_slice[6..],
266                 )?;
267                 decode_chunk(
268                     &input_slice[16..],
269                     input_index + 16,
270                     decode_table,
271                     &mut output_slice[12..],
272                 )?;
273                 decode_chunk(
274                     &input_slice[24..],
275                     input_index + 24,
276                     decode_table,
277                     &mut output_slice[18..],
278                 )?;
279 
280                 input_index += INPUT_BLOCK_LEN;
281                 output_index += DECODED_BLOCK_LEN - DECODED_CHUNK_SUFFIX;
282                 remaining_chunks -= CHUNKS_PER_FAST_LOOP_BLOCK;
283             }
284         }
285 
286         // Fast loop, stage 2 (aka still pretty fast loop)
287         // 8 bytes at a time for whatever we didn't do in stage 1.
288         if let Some(max_start_index) = length_of_fast_decode_chunks.checked_sub(INPUT_CHUNK_LEN) {
289             while input_index < max_start_index {
290                 decode_chunk(
291                     &input[input_index..(input_index + INPUT_CHUNK_LEN)],
292                     input_index,
293                     decode_table,
294                     &mut output
295                         [output_index..(output_index + DECODED_CHUNK_LEN + DECODED_CHUNK_SUFFIX)],
296                 )?;
297 
298                 output_index += DECODED_CHUNK_LEN;
299                 input_index += INPUT_CHUNK_LEN;
300                 remaining_chunks -= 1;
301             }
302         }
303     }
304 
305     // Stage 3
306     // If input length was such that a chunk had to be deferred until after the fast loop
307     // because decoding it would have produced 2 trailing bytes that wouldn't then be
308     // overwritten, we decode that chunk here. This way is slower but doesn't write the 2
309     // trailing bytes.
310     // However, we still need to avoid the last chunk (partial or complete) because it could
311     // have padding, so we always do 1 fewer to avoid the last chunk.
312     for _ in 1..remaining_chunks {
313         decode_chunk_precise(
314             &input[input_index..],
315             input_index,
316             decode_table,
317             &mut output[output_index..(output_index + DECODED_CHUNK_LEN)],
318         )?;
319 
320         input_index += INPUT_CHUNK_LEN;
321         output_index += DECODED_CHUNK_LEN;
322     }
323 
324     // Stage 4
325     // Finally, decode any leftovers that aren't a complete input block of 8 bytes.
326     // Use a u64 as a stack-resident 8 byte buffer.
327     let mut leftover_bits: u64 = 0;
328     let mut morsels_in_leftover = 0;
329     let mut padding_bytes = 0;
330     let mut first_padding_index: usize = 0;
331     let start_of_leftovers = input_index;
332     for (i, b) in input[start_of_leftovers..].iter().enumerate() {
333         // '=' padding
334         if *b == 0x3D {
335             // There can be bad padding in a few ways:
336             // 1 - Padding with non-padding characters after it
337             // 2 - Padding after zero or one non-padding characters before it
338             //     in the current quad.
339             // 3 - More than two characters of padding. If 3 or 4 padding chars
340             //     are in the same quad, that implies it will be caught by #2.
341             //     If it spreads from one quad to another, it will be caught by
342             //     #2 in the second quad.
343 
344             if i % 4 < 2 {
345                 // Check for case #2.
346                 let bad_padding_index = start_of_leftovers + if padding_bytes > 0 {
347                     // If we've already seen padding, report the first padding index.
348                     // This is to be consistent with the faster logic above: it will report an
349                     // error on the first padding character (since it doesn't expect to see
350                     // anything but actual encoded data).
351                     first_padding_index
352                 } else {
353                     // haven't seen padding before, just use where we are now
354                     i
355                 };
356                 return Err(DecodeError::InvalidByte(bad_padding_index, *b));
357             }
358 
359             if padding_bytes == 0 {
360                 first_padding_index = i;
361             }
362 
363             padding_bytes += 1;
364             continue;
365         }
366 
367         // Check for case #1.
368         // To make '=' handling consistent with the main loop, don't allow
369         // non-suffix '=' in trailing chunk either. Report error as first
370         // erroneous padding.
371         if padding_bytes > 0 {
372             return Err(DecodeError::InvalidByte(
373                 start_of_leftovers + first_padding_index,
374                 0x3D,
375             ));
376         }
377 
378         // can use up to 8 * 6 = 48 bits of the u64, if last chunk has no padding.
379         // To minimize shifts, pack the leftovers from left to right.
380         let shift = 64 - (morsels_in_leftover + 1) * 6;
381         // tables are all 256 elements, lookup with a u8 index always succeeds
382         let morsel = decode_table[*b as usize];
383         if morsel == tables::INVALID_VALUE {
384             return Err(DecodeError::InvalidByte(start_of_leftovers + i, *b));
385         }
386 
387         leftover_bits |= (morsel as u64) << shift;
388         morsels_in_leftover += 1;
389     }
390 
391     let leftover_bits_ready_to_append = match morsels_in_leftover {
392         0 => 0,
393         2 => 8,
394         3 => 16,
395         4 => 24,
396         6 => 32,
397         7 => 40,
398         8 => 48,
399         _ => unreachable!(
400             "Impossible: must only have 0 to 8 input bytes in last chunk, with no invalid lengths"
401         ),
402     };
403 
404     let mut leftover_bits_appended_to_buf = 0;
405     while leftover_bits_appended_to_buf < leftover_bits_ready_to_append {
406         // `as` simply truncates the higher bits, which is what we want here
407         let selected_bits = (leftover_bits >> (56 - leftover_bits_appended_to_buf)) as u8;
408         output[output_index] = selected_bits;
409         output_index += 1;
410 
411         leftover_bits_appended_to_buf += 8;
412     }
413 
414     Ok(output_index)
415 }
416 
417 /// Decode 8 bytes of input into 6 bytes of output. 8 bytes of output will be written, but only the
418 /// first 6 of those contain meaningful data.
419 ///
420 /// `input` is the bytes to decode, of which the first 8 bytes will be processed.
421 /// `index_at_start_of_input` is the offset in the overall input (used for reporting errors
422 /// accurately)
423 /// `decode_table` is the lookup table for the particular base64 alphabet.
424 /// `output` will have its first 8 bytes overwritten, of which only the first 6 are valid decoded
425 /// data.
426 // yes, really inline (worth 30-50% speedup)
427 #[inline(always)]
decode_chunk( input: &[u8], index_at_start_of_input: usize, decode_table: &[u8; 256], output: &mut [u8], ) -> Result<(), DecodeError>428 fn decode_chunk(
429     input: &[u8],
430     index_at_start_of_input: usize,
431     decode_table: &[u8; 256],
432     output: &mut [u8],
433 ) -> Result<(), DecodeError> {
434     let mut accum: u64;
435 
436     let morsel = decode_table[input[0] as usize];
437     if morsel == tables::INVALID_VALUE {
438         return Err(DecodeError::InvalidByte(index_at_start_of_input, input[0]));
439     }
440     accum = (morsel as u64) << 58;
441 
442     let morsel = decode_table[input[1] as usize];
443     if morsel == tables::INVALID_VALUE {
444         return Err(DecodeError::InvalidByte(
445             index_at_start_of_input + 1,
446             input[1],
447         ));
448     }
449     accum |= (morsel as u64) << 52;
450 
451     let morsel = decode_table[input[2] as usize];
452     if morsel == tables::INVALID_VALUE {
453         return Err(DecodeError::InvalidByte(
454             index_at_start_of_input + 2,
455             input[2],
456         ));
457     }
458     accum |= (morsel as u64) << 46;
459 
460     let morsel = decode_table[input[3] as usize];
461     if morsel == tables::INVALID_VALUE {
462         return Err(DecodeError::InvalidByte(
463             index_at_start_of_input + 3,
464             input[3],
465         ));
466     }
467     accum |= (morsel as u64) << 40;
468 
469     let morsel = decode_table[input[4] as usize];
470     if morsel == tables::INVALID_VALUE {
471         return Err(DecodeError::InvalidByte(
472             index_at_start_of_input + 4,
473             input[4],
474         ));
475     }
476     accum |= (morsel as u64) << 34;
477 
478     let morsel = decode_table[input[5] as usize];
479     if morsel == tables::INVALID_VALUE {
480         return Err(DecodeError::InvalidByte(
481             index_at_start_of_input + 5,
482             input[5],
483         ));
484     }
485     accum |= (morsel as u64) << 28;
486 
487     let morsel = decode_table[input[6] as usize];
488     if morsel == tables::INVALID_VALUE {
489         return Err(DecodeError::InvalidByte(
490             index_at_start_of_input + 6,
491             input[6],
492         ));
493     }
494     accum |= (morsel as u64) << 22;
495 
496     let morsel = decode_table[input[7] as usize];
497     if morsel == tables::INVALID_VALUE {
498         return Err(DecodeError::InvalidByte(
499             index_at_start_of_input + 7,
500             input[7],
501         ));
502     }
503     accum |= (morsel as u64) << 16;
504 
505     BigEndian::write_u64(output, accum);
506 
507     Ok(())
508 }
509 
510 /// Decode an 8-byte chunk, but only write the 6 bytes actually decoded instead of including 2
511 /// trailing garbage bytes.
512 #[inline]
decode_chunk_precise( input: &[u8], index_at_start_of_input: usize, decode_table: &[u8; 256], output: &mut [u8], ) -> Result<(), DecodeError>513 fn decode_chunk_precise(
514     input: &[u8],
515     index_at_start_of_input: usize,
516     decode_table: &[u8; 256],
517     output: &mut [u8],
518 ) -> Result<(), DecodeError> {
519     let mut tmp_buf = [0_u8; 8];
520 
521     decode_chunk(
522         input,
523         index_at_start_of_input,
524         decode_table,
525         &mut tmp_buf[..],
526     )?;
527 
528     output[0..6].copy_from_slice(&tmp_buf[0..6]);
529 
530     Ok(())
531 }
532 
533 #[cfg(test)]
534 mod tests {
535     extern crate rand;
536 
537     use super::*;
538     use encode::encode_config_buf;
539     use tests::{assert_encode_sanity, random_config};
540 
541     use self::rand::distributions::{IndependentSample, Range};
542     use self::rand::Rng;
543 
544     #[test]
decode_chunk_precise_writes_only_6_bytes()545     fn decode_chunk_precise_writes_only_6_bytes() {
546         let input = b"Zm9vYmFy"; // "foobar"
547         let mut output = [0_u8, 1, 2, 3, 4, 5, 6, 7];
548         decode_chunk_precise(&input[..], 0, tables::STANDARD_DECODE, &mut output).unwrap();
549         assert_eq!(&vec![b'f', b'o', b'o', b'b', b'a', b'r', 6, 7], &output);
550     }
551 
552     #[test]
decode_chunk_writes_8_bytes()553     fn decode_chunk_writes_8_bytes() {
554         let input = b"Zm9vYmFy"; // "foobar"
555         let mut output = [0_u8, 1, 2, 3, 4, 5, 6, 7];
556         decode_chunk(&input[..], 0, tables::STANDARD_DECODE, &mut output).unwrap();
557         assert_eq!(&vec![b'f', b'o', b'o', b'b', b'a', b'r', 0, 0], &output);
558     }
559 
560     #[test]
decode_into_nonempty_vec_doesnt_clobber_existing_prefix()561     fn decode_into_nonempty_vec_doesnt_clobber_existing_prefix() {
562         let mut orig_data = Vec::new();
563         let mut encoded_data = String::new();
564         let mut decoded_with_prefix = Vec::new();
565         let mut decoded_without_prefix = Vec::new();
566         let mut prefix = Vec::new();
567 
568         let prefix_len_range = Range::new(0, 1000);
569         let input_len_range = Range::new(0, 1000);
570         let line_len_range = Range::new(1, 1000);
571 
572         let mut rng = rand::weak_rng();
573 
574         for _ in 0..10_000 {
575             orig_data.clear();
576             encoded_data.clear();
577             decoded_with_prefix.clear();
578             decoded_without_prefix.clear();
579             prefix.clear();
580 
581             let input_len = input_len_range.ind_sample(&mut rng);
582 
583             for _ in 0..input_len {
584                 orig_data.push(rng.gen());
585             }
586 
587             let config = random_config(&mut rng, &line_len_range);
588             encode_config_buf(&orig_data, config, &mut encoded_data);
589             assert_encode_sanity(&encoded_data, &config, input_len);
590 
591             let prefix_len = prefix_len_range.ind_sample(&mut rng);
592 
593             // fill the buf with a prefix
594             for _ in 0..prefix_len {
595                 prefix.push(rng.gen());
596             }
597 
598             decoded_with_prefix.resize(prefix_len, 0);
599             decoded_with_prefix.copy_from_slice(&prefix);
600 
601             // decode into the non-empty buf
602             decode_config_buf(&encoded_data, config, &mut decoded_with_prefix).unwrap();
603             // also decode into the empty buf
604             decode_config_buf(&encoded_data, config, &mut decoded_without_prefix).unwrap();
605 
606             assert_eq!(
607                 prefix_len + decoded_without_prefix.len(),
608                 decoded_with_prefix.len()
609             );
610             assert_eq!(orig_data, decoded_without_prefix);
611 
612             // append plain decode onto prefix
613             prefix.append(&mut decoded_without_prefix);
614 
615             assert_eq!(prefix, decoded_with_prefix);
616         }
617     }
618 
619     #[test]
decode_into_slice_doesnt_clobber_existing_prefix_or_suffix()620     fn decode_into_slice_doesnt_clobber_existing_prefix_or_suffix() {
621         let mut orig_data = Vec::new();
622         let mut encoded_data = String::new();
623         let mut decode_buf = Vec::new();
624         let mut decode_buf_copy: Vec<u8> = Vec::new();
625 
626         let input_len_range = Range::new(0, 1000);
627         let line_len_range = Range::new(1, 1000);
628 
629         let mut rng = rand::weak_rng();
630 
631         for _ in 0..10_000 {
632             orig_data.clear();
633             encoded_data.clear();
634             decode_buf.clear();
635             decode_buf_copy.clear();
636 
637             let input_len = input_len_range.ind_sample(&mut rng);
638 
639             for _ in 0..input_len {
640                 orig_data.push(rng.gen());
641             }
642 
643             let config = random_config(&mut rng, &line_len_range);
644             encode_config_buf(&orig_data, config, &mut encoded_data);
645             assert_encode_sanity(&encoded_data, &config, input_len);
646 
647             // fill the buffer with random garbage, long enough to have some room before and after
648             for _ in 0..5000 {
649                 decode_buf.push(rng.gen());
650             }
651 
652             // keep a copy for later comparison
653             decode_buf_copy.extend(decode_buf.iter());
654 
655             let offset = 1000;
656 
657             // decode into the non-empty buf
658             let decode_bytes_written =
659                 decode_config_slice(&encoded_data, config, &mut decode_buf[offset..]).unwrap();
660 
661             assert_eq!(orig_data.len(), decode_bytes_written);
662             assert_eq!(
663                 orig_data,
664                 &decode_buf[offset..(offset + decode_bytes_written)]
665             );
666             assert_eq!(&decode_buf_copy[0..offset], &decode_buf[0..offset]);
667             assert_eq!(
668                 &decode_buf_copy[offset + decode_bytes_written..],
669                 &decode_buf[offset + decode_bytes_written..]
670             );
671         }
672     }
673 
674     #[test]
decode_into_slice_fits_in_precisely_sized_slice()675     fn decode_into_slice_fits_in_precisely_sized_slice() {
676         let mut orig_data = Vec::new();
677         let mut encoded_data = String::new();
678         let mut decode_buf = Vec::new();
679 
680         let input_len_range = Range::new(0, 1000);
681         let line_len_range = Range::new(1, 1000);
682 
683         let mut rng = rand::weak_rng();
684 
685         for _ in 0..10_000 {
686             orig_data.clear();
687             encoded_data.clear();
688             decode_buf.clear();
689 
690             let input_len = input_len_range.ind_sample(&mut rng);
691 
692             for _ in 0..input_len {
693                 orig_data.push(rng.gen());
694             }
695 
696             let config = random_config(&mut rng, &line_len_range);
697             encode_config_buf(&orig_data, config, &mut encoded_data);
698             assert_encode_sanity(&encoded_data, &config, input_len);
699 
700             decode_buf.resize(input_len, 0);
701 
702             // decode into the non-empty buf
703             let decode_bytes_written =
704                 decode_config_slice(&encoded_data, config, &mut decode_buf[..]).unwrap();
705 
706             assert_eq!(orig_data.len(), decode_bytes_written);
707             assert_eq!(orig_data, decode_buf);
708         }
709     }
710 }
711