flate2_expose/
mem.rs

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