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