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