• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::error::Error;
2 use std::fmt;
3 use std::io;
4 use std::slice;
5 
6 use crate::ffi::{self, Backend, Deflate, DeflateBackend, Inflate, InflateBackend};
7 use crate::Compression;
8 
9 /// Raw in-memory compression stream for blocks of data.
10 ///
11 /// This type is the building block for the I/O streams in the rest of this
12 /// crate. It requires more management than the [`Read`]/[`Write`] API but is
13 /// maximally flexible in terms of accepting input from any source and being
14 /// able to produce output to any memory location.
15 ///
16 /// It is recommended to use the I/O stream adaptors over this type as they're
17 /// easier to use.
18 ///
19 /// [`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
20 /// [`Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
21 #[derive(Debug)]
22 pub struct Compress {
23     inner: Deflate,
24 }
25 
26 /// Raw in-memory decompression stream for blocks of data.
27 ///
28 /// This type is the building block for the I/O streams in the rest of this
29 /// crate. It requires more management than the [`Read`]/[`Write`] API but is
30 /// maximally flexible in terms of accepting input from any source and being
31 /// able to produce output to any memory location.
32 ///
33 /// It is recommended to use the I/O stream adaptors over this type as they're
34 /// easier to use.
35 ///
36 /// [`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
37 /// [`Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
38 #[derive(Debug)]
39 pub struct Decompress {
40     inner: Inflate,
41 }
42 
43 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
44 /// Values which indicate the form of flushing to be used when compressing
45 /// in-memory data.
46 pub enum FlushCompress {
47     /// A typical parameter for passing to compression/decompression functions,
48     /// this indicates that the underlying stream to decide how much data to
49     /// accumulate before producing output in order to maximize compression.
50     None = ffi::MZ_NO_FLUSH as isize,
51 
52     /// All pending output is flushed to the output buffer and the output is
53     /// aligned on a byte boundary so that the decompressor can get all input
54     /// data available so far.
55     ///
56     /// Flushing may degrade compression for some compression algorithms and so
57     /// it should only be used when necessary. This will complete the current
58     /// deflate block and follow it with an empty stored block.
59     Sync = ffi::MZ_SYNC_FLUSH as isize,
60 
61     /// All pending output is flushed to the output buffer, but the output is
62     /// not aligned to a byte boundary.
63     ///
64     /// All of the input data so far will be available to the decompressor (as
65     /// with `Flush::Sync`. This completes the current deflate block and follows
66     /// it with an empty fixed codes block that is 10 bites long, and it assures
67     /// that enough bytes are output in order for the decompessor to finish the
68     /// block before the empty fixed code block.
69     Partial = ffi::MZ_PARTIAL_FLUSH as isize,
70 
71     /// All output is flushed as with `Flush::Sync` and the compression state is
72     /// reset so decompression can restart from this point if previous
73     /// compressed data has been damaged or if random access is desired.
74     ///
75     /// Using this option too often can seriously degrade compression.
76     Full = ffi::MZ_FULL_FLUSH as isize,
77 
78     /// Pending input is processed and pending output is flushed.
79     ///
80     /// The return value may indicate that the stream is not yet done and more
81     /// data has yet to be processed.
82     Finish = ffi::MZ_FINISH as isize,
83 
84     #[doc(hidden)]
85     _Nonexhaustive,
86 }
87 
88 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
89 /// Values which indicate the form of flushing to be used when
90 /// decompressing in-memory data.
91 pub enum FlushDecompress {
92     /// A typical parameter for passing to compression/decompression functions,
93     /// this indicates that the underlying stream to decide how much data to
94     /// accumulate before producing output in order to maximize compression.
95     None = ffi::MZ_NO_FLUSH as isize,
96 
97     /// All pending output is flushed to the output buffer and the output is
98     /// aligned on a byte boundary so that the decompressor can get all input
99     /// data available so far.
100     ///
101     /// Flushing may degrade compression for some compression algorithms and so
102     /// it should only be used when necessary. This will complete the current
103     /// deflate block and follow it with an empty stored block.
104     Sync = ffi::MZ_SYNC_FLUSH as isize,
105 
106     /// Pending input is processed and pending output is flushed.
107     ///
108     /// The return value may indicate that the stream is not yet done and more
109     /// data has yet to be processed.
110     Finish = ffi::MZ_FINISH as isize,
111 
112     #[doc(hidden)]
113     _Nonexhaustive,
114 }
115 
116 /// The inner state for an error when decompressing
117 #[derive(Debug, Default)]
118 pub(crate) struct DecompressErrorInner {
119     pub(crate) needs_dictionary: Option<u32>,
120 }
121 
122 /// Error returned when a decompression object finds that the input stream of
123 /// bytes was not a valid input stream of bytes.
124 #[derive(Debug)]
125 pub struct DecompressError(pub(crate) DecompressErrorInner);
126 
127 impl DecompressError {
128     /// Indicates whether decompression failed due to requiring a dictionary.
129     ///
130     /// The resulting integer is the Adler-32 checksum of the dictionary
131     /// required.
needs_dictionary(&self) -> Option<u32>132     pub fn needs_dictionary(&self) -> Option<u32> {
133         self.0.needs_dictionary
134     }
135 }
136 
137 #[inline]
decompress_failed() -> Result<Status, DecompressError>138 pub(crate) fn decompress_failed() -> Result<Status, DecompressError> {
139     Err(DecompressError(Default::default()))
140 }
141 
142 #[inline]
decompress_need_dict(adler: u32) -> Result<Status, DecompressError>143 pub(crate) fn decompress_need_dict(adler: u32) -> Result<Status, DecompressError> {
144     Err(DecompressError(DecompressErrorInner {
145         needs_dictionary: Some(adler),
146     }))
147 }
148 
149 /// Error returned when a compression object is used incorrectly or otherwise
150 /// generates an error.
151 #[derive(Debug)]
152 pub struct CompressError(pub(crate) ());
153 
154 /// Possible status results of compressing some data or successfully
155 /// decompressing a block of data.
156 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
157 pub enum Status {
158     /// Indicates success.
159     ///
160     /// Means that more input may be needed but isn't available
161     /// and/or there's more output to be written but the output buffer is full.
162     Ok,
163 
164     /// Indicates that forward progress is not possible due to input or output
165     /// buffers being empty.
166     ///
167     /// For compression it means the input buffer needs some more data or the
168     /// output buffer needs to be freed up before trying again.
169     ///
170     /// For decompression this means that more input is needed to continue or
171     /// the output buffer isn't large enough to contain the result. The function
172     /// can be called again after fixing both.
173     BufError,
174 
175     /// Indicates that all input has been consumed and all output bytes have
176     /// been written. Decompression/compression should not be called again.
177     ///
178     /// For decompression with zlib streams the adler-32 of the decompressed
179     /// data has also been verified.
180     StreamEnd,
181 }
182 
183 impl Compress {
184     /// Creates a new object ready for compressing data that it's given.
185     ///
186     /// The `level` argument here indicates what level of compression is going
187     /// to be performed, and the `zlib_header` argument indicates whether the
188     /// output data should have a zlib header or not.
new(level: Compression, zlib_header: bool) -> Compress189     pub fn new(level: Compression, zlib_header: bool) -> Compress {
190         Compress {
191             inner: Deflate::make(level, zlib_header, ffi::MZ_DEFAULT_WINDOW_BITS as u8),
192         }
193     }
194 
195     /// Creates a new object ready for compressing data that it's given.
196     ///
197     /// The `level` argument here indicates what level of compression is going
198     /// to be performed, and the `zlib_header` argument indicates whether the
199     /// output data should have a zlib header or not. The `window_bits` parameter
200     /// indicates the base-2 logarithm of the sliding window size and must be
201     /// between 9 and 15.
202     ///
203     /// # Panics
204     ///
205     /// If `window_bits` does not fall into the range 9 ..= 15,
206     /// `new_with_window_bits` will panic.
207     ///
208     /// # Note
209     ///
210     /// This constructor is only available when the `zlib` feature is used.
211     /// Other backends currently do not support custom window bits.
212     #[cfg(feature = "any_zlib")]
new_with_window_bits( level: Compression, zlib_header: bool, window_bits: u8, ) -> Compress213     pub fn new_with_window_bits(
214         level: Compression,
215         zlib_header: bool,
216         window_bits: u8,
217     ) -> Compress {
218         assert!(
219             window_bits > 8 && window_bits < 16,
220             "window_bits must be within 9 ..= 15"
221         );
222         Compress {
223             inner: Deflate::make(level, zlib_header, window_bits),
224         }
225     }
226 
227     /// Creates a new object ready for compressing data that it's given.
228     ///
229     /// The `level` argument here indicates what level of compression is going
230     /// to be performed.
231     ///
232     /// The Compress object produced by this constructor outputs gzip headers
233     /// for the compressed data.
234     ///
235     /// # Panics
236     ///
237     /// If `window_bits` does not fall into the range 9 ..= 15,
238     /// `new_with_window_bits` will panic.
239     ///
240     /// # Note
241     ///
242     /// This constructor is only available when the `zlib` feature is used.
243     /// Other backends currently do not support gzip headers for Compress.
244     #[cfg(feature = "zlib")]
new_gzip(level: Compression, window_bits: u8) -> Compress245     pub fn new_gzip(level: Compression, window_bits: u8) -> Compress {
246         assert!(
247             window_bits > 8 && window_bits < 16,
248             "window_bits must be within 9 ..= 15"
249         );
250         Compress {
251             inner: Deflate::make(level, true, window_bits + 16),
252         }
253     }
254 
255     /// Returns the total number of input bytes which have been processed by
256     /// this compression object.
total_in(&self) -> u64257     pub fn total_in(&self) -> u64 {
258         self.inner.total_in()
259     }
260 
261     /// Returns the total number of output bytes which have been produced by
262     /// this compression object.
total_out(&self) -> u64263     pub fn total_out(&self) -> u64 {
264         self.inner.total_out()
265     }
266 
267     /// Specifies the compression dictionary to use.
268     ///
269     /// Returns the Adler-32 checksum of the dictionary.
270     #[cfg(feature = "any_zlib")]
set_dictionary(&mut self, dictionary: &[u8]) -> Result<u32, CompressError>271     pub fn set_dictionary(&mut self, dictionary: &[u8]) -> Result<u32, CompressError> {
272         let stream = &mut *self.inner.inner.stream_wrapper;
273         let rc = unsafe {
274             assert!(dictionary.len() < ffi::uInt::max_value() as usize);
275             ffi::deflateSetDictionary(stream, dictionary.as_ptr(), dictionary.len() as ffi::uInt)
276         };
277 
278         match rc {
279             ffi::MZ_STREAM_ERROR => Err(CompressError(())),
280             ffi::MZ_OK => Ok(stream.adler as u32),
281             c => panic!("unknown return code: {}", c),
282         }
283     }
284 
285     /// Quickly resets this compressor without having to reallocate anything.
286     ///
287     /// This is equivalent to dropping this object and then creating a new one.
reset(&mut self)288     pub fn reset(&mut self) {
289         self.inner.reset();
290     }
291 
292     /// Dynamically updates the compression level.
293     ///
294     /// This can be used to switch between compression levels for different
295     /// kinds of data, or it can be used in conjunction with a call to reset
296     /// to reuse the compressor.
297     ///
298     /// This may return an error if there wasn't enough output space to complete
299     /// the compression of the available input data before changing the
300     /// compression level. Flushing the stream before calling this method
301     /// ensures that the function will succeed on the first call.
302     #[cfg(feature = "any_zlib")]
set_level(&mut self, level: Compression) -> Result<(), CompressError>303     pub fn set_level(&mut self, level: Compression) -> Result<(), CompressError> {
304         use libc::c_int;
305         let stream = &mut *self.inner.inner.stream_wrapper;
306 
307         let rc = unsafe { ffi::deflateParams(stream, level.0 as c_int, ffi::MZ_DEFAULT_STRATEGY) };
308 
309         match rc {
310             ffi::MZ_OK => Ok(()),
311             ffi::MZ_BUF_ERROR => Err(CompressError(())),
312             c => panic!("unknown return code: {}", c),
313         }
314     }
315 
316     /// Compresses the input data into the output, consuming only as much
317     /// input as needed and writing as much output as possible.
318     ///
319     /// The flush option can be any of the available `FlushCompress` parameters.
320     ///
321     /// To learn how much data was consumed or how much output was produced, use
322     /// the `total_in` and `total_out` functions before/after this is called.
compress( &mut self, input: &[u8], output: &mut [u8], flush: FlushCompress, ) -> Result<Status, CompressError>323     pub fn compress(
324         &mut self,
325         input: &[u8],
326         output: &mut [u8],
327         flush: FlushCompress,
328     ) -> Result<Status, CompressError> {
329         self.inner.compress(input, output, flush)
330     }
331 
332     /// Compresses the input data into the extra space of the output, consuming
333     /// only as much input as needed and writing as much output as possible.
334     ///
335     /// This function has the same semantics as `compress`, except that the
336     /// length of `vec` is managed by this function. This will not reallocate
337     /// the vector provided or attempt to grow it, so space for the output must
338     /// be reserved in the output vector by the caller before calling this
339     /// function.
compress_vec( &mut self, input: &[u8], output: &mut Vec<u8>, flush: FlushCompress, ) -> Result<Status, CompressError>340     pub fn compress_vec(
341         &mut self,
342         input: &[u8],
343         output: &mut Vec<u8>,
344         flush: FlushCompress,
345     ) -> Result<Status, CompressError> {
346         let cap = output.capacity();
347         let len = output.len();
348 
349         unsafe {
350             let before = self.total_out();
351             let ret = {
352                 let ptr = output.as_mut_ptr().offset(len as isize);
353                 let out = slice::from_raw_parts_mut(ptr, cap - len);
354                 self.compress(input, out, flush)
355             };
356             output.set_len((self.total_out() - before) as usize + len);
357             return ret;
358         }
359     }
360 }
361 
362 impl Decompress {
363     /// Creates a new object ready for decompressing data that it's given.
364     ///
365     /// The `zlib_header` argument indicates whether the input data is expected
366     /// to have a zlib header or not.
new(zlib_header: bool) -> Decompress367     pub fn new(zlib_header: bool) -> Decompress {
368         Decompress {
369             inner: Inflate::make(zlib_header, ffi::MZ_DEFAULT_WINDOW_BITS as u8),
370         }
371     }
372 
373     /// Creates a new object ready for decompressing data that it's given.
374     ///
375     /// The `zlib_header` argument indicates whether the input data is expected
376     /// to have a zlib header or not. The `window_bits` parameter indicates the
377     /// base-2 logarithm of the sliding window size and must be between 9 and 15.
378     ///
379     /// # Panics
380     ///
381     /// If `window_bits` does not fall into the range 9 ..= 15,
382     /// `new_with_window_bits` will panic.
383     ///
384     /// # Note
385     ///
386     /// This constructor is only available when the `zlib` feature is used.
387     /// Other backends currently do not support custom window bits.
388     #[cfg(feature = "any_zlib")]
new_with_window_bits(zlib_header: bool, window_bits: u8) -> Decompress389     pub fn new_with_window_bits(zlib_header: bool, window_bits: u8) -> Decompress {
390         assert!(
391             window_bits > 8 && window_bits < 16,
392             "window_bits must be within 9 ..= 15"
393         );
394         Decompress {
395             inner: Inflate::make(zlib_header, window_bits),
396         }
397     }
398 
399     /// Creates a new object ready for decompressing data that it's given.
400     ///
401     /// The Deompress object produced by this constructor expects gzip headers
402     /// for the compressed data.
403     ///
404     /// # Panics
405     ///
406     /// If `window_bits` does not fall into the range 9 ..= 15,
407     /// `new_with_window_bits` will panic.
408     ///
409     /// # Note
410     ///
411     /// This constructor is only available when the `zlib` feature is used.
412     /// Other backends currently do not support gzip headers for Decompress.
413     #[cfg(feature = "zlib")]
new_gzip(window_bits: u8) -> Decompress414     pub fn new_gzip(window_bits: u8) -> Decompress {
415         assert!(
416             window_bits > 8 && window_bits < 16,
417             "window_bits must be within 9 ..= 15"
418         );
419         Decompress {
420             inner: Inflate::make(true, window_bits + 16),
421         }
422     }
423 
424     /// Returns the total number of input bytes which have been processed by
425     /// this decompression object.
total_in(&self) -> u64426     pub fn total_in(&self) -> u64 {
427         self.inner.total_in()
428     }
429 
430     /// Returns the total number of output bytes which have been produced by
431     /// this decompression object.
total_out(&self) -> u64432     pub fn total_out(&self) -> u64 {
433         self.inner.total_out()
434     }
435 
436     /// Decompresses the input data into the output, consuming only as much
437     /// input as needed and writing as much output as possible.
438     ///
439     /// The flush option can be any of the available `FlushDecompress` parameters.
440     ///
441     /// If the first call passes `FlushDecompress::Finish` it is assumed that
442     /// the input and output buffers are both sized large enough to decompress
443     /// the entire stream in a single call.
444     ///
445     /// A flush value of `FlushDecompress::Finish` indicates that there are no
446     /// more source bytes available beside what's already in the input buffer,
447     /// and the output buffer is large enough to hold the rest of the
448     /// decompressed data.
449     ///
450     /// To learn how much data was consumed or how much output was produced, use
451     /// the `total_in` and `total_out` functions before/after this is called.
452     ///
453     /// # Errors
454     ///
455     /// If the input data to this instance of `Decompress` is not a valid
456     /// zlib/deflate stream then this function may return an instance of
457     /// `DecompressError` to indicate that the stream of input bytes is corrupted.
decompress( &mut self, input: &[u8], output: &mut [u8], flush: FlushDecompress, ) -> Result<Status, DecompressError>458     pub fn decompress(
459         &mut self,
460         input: &[u8],
461         output: &mut [u8],
462         flush: FlushDecompress,
463     ) -> Result<Status, DecompressError> {
464         self.inner.decompress(input, output, flush)
465     }
466 
467     /// Decompresses the input data into the extra space in the output vector
468     /// specified by `output`.
469     ///
470     /// This function has the same semantics as `decompress`, except that the
471     /// length of `vec` is managed by this function. This will not reallocate
472     /// the vector provided or attempt to grow it, so space for the output must
473     /// be reserved in the output vector by the caller before calling this
474     /// function.
475     ///
476     /// # Errors
477     ///
478     /// If the input data to this instance of `Decompress` is not a valid
479     /// zlib/deflate stream then this function may return an instance of
480     /// `DecompressError` to indicate that the stream of input bytes is corrupted.
decompress_vec( &mut self, input: &[u8], output: &mut Vec<u8>, flush: FlushDecompress, ) -> Result<Status, DecompressError>481     pub fn decompress_vec(
482         &mut self,
483         input: &[u8],
484         output: &mut Vec<u8>,
485         flush: FlushDecompress,
486     ) -> Result<Status, DecompressError> {
487         let cap = output.capacity();
488         let len = output.len();
489 
490         unsafe {
491             let before = self.total_out();
492             let ret = {
493                 let ptr = output.as_mut_ptr().offset(len as isize);
494                 let out = slice::from_raw_parts_mut(ptr, cap - len);
495                 self.decompress(input, out, flush)
496             };
497             output.set_len((self.total_out() - before) as usize + len);
498             return ret;
499         }
500     }
501 
502     /// Specifies the decompression dictionary to use.
503     #[cfg(feature = "any_zlib")]
set_dictionary(&mut self, dictionary: &[u8]) -> Result<u32, DecompressError>504     pub fn set_dictionary(&mut self, dictionary: &[u8]) -> Result<u32, DecompressError> {
505         let stream = &mut *self.inner.inner.stream_wrapper;
506         let rc = unsafe {
507             assert!(dictionary.len() < ffi::uInt::max_value() as usize);
508             ffi::inflateSetDictionary(stream, dictionary.as_ptr(), dictionary.len() as ffi::uInt)
509         };
510 
511         match rc {
512             ffi::MZ_STREAM_ERROR => Err(DecompressError(Default::default())),
513             ffi::MZ_DATA_ERROR => Err(DecompressError(DecompressErrorInner {
514                 needs_dictionary: Some(stream.adler as u32),
515             })),
516             ffi::MZ_OK => Ok(stream.adler as u32),
517             c => panic!("unknown return code: {}", c),
518         }
519     }
520 
521     /// Performs the equivalent of replacing this decompression state with a
522     /// freshly allocated copy.
523     ///
524     /// This function may not allocate memory, though, and attempts to reuse any
525     /// previously existing resources.
526     ///
527     /// The argument provided here indicates whether the reset state will
528     /// attempt to decode a zlib header first or not.
reset(&mut self, zlib_header: bool)529     pub fn reset(&mut self, zlib_header: bool) {
530         self.inner.reset(zlib_header);
531     }
532 }
533 
534 impl Error for DecompressError {}
535 
536 impl From<DecompressError> for io::Error {
from(data: DecompressError) -> io::Error537     fn from(data: DecompressError) -> io::Error {
538         io::Error::new(io::ErrorKind::Other, data)
539     }
540 }
541 
542 impl fmt::Display for DecompressError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result543     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
544         write!(f, "deflate decompression error")
545     }
546 }
547 
548 impl Error for CompressError {}
549 
550 impl From<CompressError> for io::Error {
from(data: CompressError) -> io::Error551     fn from(data: CompressError) -> io::Error {
552         io::Error::new(io::ErrorKind::Other, data)
553     }
554 }
555 
556 impl fmt::Display for CompressError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result557     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
558         write!(f, "deflate decompression error")
559     }
560 }
561 
562 #[cfg(test)]
563 mod tests {
564     use std::io::Write;
565 
566     use crate::write;
567     use crate::{Compression, Decompress, FlushDecompress};
568 
569     #[cfg(feature = "any_zlib")]
570     use crate::{Compress, FlushCompress};
571 
572     #[test]
issue51()573     fn issue51() {
574         let data = vec![
575             0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xb3, 0xc9, 0x28, 0xc9,
576             0xcd, 0xb1, 0xe3, 0xe5, 0xb2, 0xc9, 0x48, 0x4d, 0x4c, 0xb1, 0xb3, 0x29, 0xc9, 0x2c,
577             0xc9, 0x49, 0xb5, 0x33, 0x31, 0x30, 0x51, 0xf0, 0xcb, 0x2f, 0x51, 0x70, 0xcb, 0x2f,
578             0xcd, 0x4b, 0xb1, 0xd1, 0x87, 0x08, 0xda, 0xe8, 0x83, 0x95, 0x00, 0x95, 0x26, 0xe5,
579             0xa7, 0x54, 0x2a, 0x24, 0xa5, 0x27, 0xe7, 0xe7, 0xe4, 0x17, 0xd9, 0x2a, 0x95, 0x67,
580             0x64, 0x96, 0xa4, 0x2a, 0x81, 0x8c, 0x48, 0x4e, 0xcd, 0x2b, 0x49, 0x2d, 0xb2, 0xb3,
581             0xc9, 0x30, 0x44, 0x37, 0x01, 0x28, 0x62, 0xa3, 0x0f, 0x95, 0x06, 0xd9, 0x05, 0x54,
582             0x04, 0xe5, 0xe5, 0xa5, 0x67, 0xe6, 0x55, 0xe8, 0x1b, 0xea, 0x99, 0xe9, 0x19, 0x21,
583             0xab, 0xd0, 0x07, 0xd9, 0x01, 0x32, 0x53, 0x1f, 0xea, 0x3e, 0x00, 0x94, 0x85, 0xeb,
584             0xe4, 0xa8, 0x00, 0x00, 0x00,
585         ];
586 
587         let mut decoded = Vec::with_capacity(data.len() * 2);
588 
589         let mut d = Decompress::new(false);
590         // decompressed whole deflate stream
591         assert!(d
592             .decompress_vec(&data[10..], &mut decoded, FlushDecompress::Finish)
593             .is_ok());
594 
595         // decompress data that has nothing to do with the deflate stream (this
596         // used to panic)
597         drop(d.decompress_vec(&[0], &mut decoded, FlushDecompress::None));
598     }
599 
600     #[test]
reset()601     fn reset() {
602         let string = "hello world".as_bytes();
603         let mut zlib = Vec::new();
604         let mut deflate = Vec::new();
605 
606         let comp = Compression::default();
607         write::ZlibEncoder::new(&mut zlib, comp)
608             .write_all(string)
609             .unwrap();
610         write::DeflateEncoder::new(&mut deflate, comp)
611             .write_all(string)
612             .unwrap();
613 
614         let mut dst = [0; 1024];
615         let mut decoder = Decompress::new(true);
616         decoder
617             .decompress(&zlib, &mut dst, FlushDecompress::Finish)
618             .unwrap();
619         assert_eq!(decoder.total_out(), string.len() as u64);
620         assert!(dst.starts_with(string));
621 
622         decoder.reset(false);
623         decoder
624             .decompress(&deflate, &mut dst, FlushDecompress::Finish)
625             .unwrap();
626         assert_eq!(decoder.total_out(), string.len() as u64);
627         assert!(dst.starts_with(string));
628     }
629 
630     #[cfg(feature = "any_zlib")]
631     #[test]
set_dictionary_with_zlib_header()632     fn set_dictionary_with_zlib_header() {
633         let string = "hello, hello!".as_bytes();
634         let dictionary = "hello".as_bytes();
635 
636         let mut encoded = Vec::with_capacity(1024);
637 
638         let mut encoder = Compress::new(Compression::default(), true);
639 
640         let dictionary_adler = encoder.set_dictionary(&dictionary).unwrap();
641 
642         encoder
643             .compress_vec(string, &mut encoded, FlushCompress::Finish)
644             .unwrap();
645 
646         assert_eq!(encoder.total_in(), string.len() as u64);
647         assert_eq!(encoder.total_out(), encoded.len() as u64);
648 
649         let mut decoder = Decompress::new(true);
650         let mut decoded = [0; 1024];
651         let decompress_error = decoder
652             .decompress(&encoded, &mut decoded, FlushDecompress::Finish)
653             .expect_err("decompression should fail due to requiring a dictionary");
654 
655         let required_adler = decompress_error.needs_dictionary()
656             .expect("the first call to decompress should indicate a dictionary is required along with the required Adler-32 checksum");
657 
658         assert_eq!(required_adler, dictionary_adler,
659             "the Adler-32 checksum should match the value when the dictionary was set on the compressor");
660 
661         let actual_adler = decoder.set_dictionary(&dictionary).unwrap();
662 
663         assert_eq!(required_adler, actual_adler);
664 
665         // Decompress the rest of the input to the remainder of the output buffer
666         let total_in = decoder.total_in();
667         let total_out = decoder.total_out();
668 
669         let decompress_result = decoder.decompress(
670             &encoded[total_in as usize..],
671             &mut decoded[total_out as usize..],
672             FlushDecompress::Finish,
673         );
674         assert!(decompress_result.is_ok());
675 
676         assert_eq!(&decoded[..decoder.total_out() as usize], string);
677     }
678 
679     #[cfg(feature = "any_zlib")]
680     #[test]
set_dictionary_raw()681     fn set_dictionary_raw() {
682         let string = "hello, hello!".as_bytes();
683         let dictionary = "hello".as_bytes();
684 
685         let mut encoded = Vec::with_capacity(1024);
686 
687         let mut encoder = Compress::new(Compression::default(), false);
688 
689         encoder.set_dictionary(&dictionary).unwrap();
690 
691         encoder
692             .compress_vec(string, &mut encoded, FlushCompress::Finish)
693             .unwrap();
694 
695         assert_eq!(encoder.total_in(), string.len() as u64);
696         assert_eq!(encoder.total_out(), encoded.len() as u64);
697 
698         let mut decoder = Decompress::new(false);
699 
700         decoder.set_dictionary(&dictionary).unwrap();
701 
702         let mut decoded = [0; 1024];
703         let decompress_result = decoder.decompress(&encoded, &mut decoded, FlushDecompress::Finish);
704 
705         assert!(decompress_result.is_ok());
706 
707         assert_eq!(&decoded[..decoder.total_out() as usize], string);
708     }
709 
710     #[cfg(feature = "zlib")]
711     #[test]
test_gzip_flate()712     fn test_gzip_flate() {
713         let string = "hello, hello!".as_bytes();
714 
715         let mut encoded = Vec::with_capacity(1024);
716 
717         let mut encoder = Compress::new_gzip(Compression::default(), 9);
718 
719         encoder
720             .compress_vec(string, &mut encoded, FlushCompress::Finish)
721             .unwrap();
722 
723         assert_eq!(encoder.total_in(), string.len() as u64);
724         assert_eq!(encoder.total_out(), encoded.len() as u64);
725 
726         let mut decoder = Decompress::new_gzip(9);
727 
728         let mut decoded = [0; 1024];
729         decoder
730             .decompress(&encoded, &mut decoded, FlushDecompress::Finish)
731             .unwrap();
732 
733         assert_eq!(&decoded[..decoder.total_out() as usize], string);
734     }
735 }
736