Skip to main content

flate2/
mem.rs

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