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}