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}