miniz_oxide/deflate/
stream.rs

1//! Extra streaming compression functionality.
2//!
3//! As of now this is mainly inteded for use to build a higher-level wrapper.
4//!
5//! There is no DeflateState as the needed state is contained in the compressor struct itself.
6use std::convert::{AsMut, AsRef};
7
8use crate::deflate::core::{compress, CompressorOxide, TDEFLFlush, TDEFLStatus};
9use crate::{MZError, MZFlush, MZStatus, StreamResult};
10
11/// Try to compress from input to output with the given Compressor
12///
13/// # Errors
14///
15/// Returns `MZError::Buf` If the size of the `output` slice is empty or no progress was made due to
16/// lack of expected input data or called after the compression was finished without
17/// MZFlush::Finish.
18///
19/// Returns `MZError::Param` if the compressor parameters are set wrong.
20pub fn deflate(
21    compressor: &mut CompressorOxide,
22    input: &[u8],
23    output: &mut [u8],
24    flush: MZFlush,
25) -> StreamResult {
26    if output.is_empty() {
27        return StreamResult::error(MZError::Buf);
28    }
29
30    if compressor.prev_return_status() == TDEFLStatus::Done {
31        return if flush == MZFlush::Finish {
32            StreamResult {
33                bytes_written: 0,
34                bytes_consumed: 0,
35                status: Ok(MZStatus::StreamEnd),
36            }
37        } else {
38            StreamResult::error(MZError::Buf)
39        };
40    }
41
42    let mut bytes_written = 0;
43    let mut bytes_consumed = 0;
44
45    let mut next_in = input.as_ref();
46    let mut next_out = output.as_mut();
47
48    let status = loop {
49        let in_bytes;
50        let out_bytes;
51        let defl_status = {
52            let res = compress(compressor, next_in, next_out, TDEFLFlush::from(flush));
53            in_bytes = res.1;
54            out_bytes = res.2;
55            res.0
56        };
57
58        next_in = &next_in[in_bytes..];
59        next_out = &mut next_out[out_bytes..];
60        bytes_consumed += in_bytes;
61        bytes_written += out_bytes;
62
63        // Check if we are done, or compression failed.
64        match defl_status {
65            TDEFLStatus::BadParam => break Err(MZError::Param),
66            // Don't think this can happen as we're not using a custom callback.
67            TDEFLStatus::PutBufFailed => break Err(MZError::Stream),
68            TDEFLStatus::Done => break Ok(MZStatus::StreamEnd),
69            _ => (),
70        };
71
72        // All the output space was used, so wait for more.
73        if next_out.is_empty() {
74            break Ok(MZStatus::Ok);
75        }
76
77        if next_in.is_empty() && (flush != MZFlush::Finish) {
78            let total_changed = bytes_written > 0 || bytes_consumed > 0;
79
80            break if (flush != MZFlush::None) || total_changed {
81                // We wrote or consumed something, and/or did a flush (sync/partial etc.).
82                Ok(MZStatus::Ok)
83            } else {
84                // No more input data, not flushing, and nothing was consumed or written,
85                // so couldn't make any progress.
86                Err(MZError::Buf)
87            };
88        }
89    };
90    StreamResult {
91        bytes_consumed,
92        bytes_written,
93        status,
94    }
95}
96
97#[cfg(test)]
98mod test {
99    use super::deflate;
100    use crate::deflate::CompressorOxide;
101    use crate::inflate::decompress_to_vec_zlib;
102    use crate::{MZFlush, MZStatus};
103    #[test]
104    fn test_state() {
105        let data = b"Hello zlib!";
106        let mut compressed = vec![0; 50];
107        let mut compressor = Box::<CompressorOxide>::default();
108        let res = deflate(&mut compressor, data, &mut compressed, MZFlush::Finish);
109        let status = res.status.expect("Failed to compress!");
110        let decomp =
111            decompress_to_vec_zlib(&compressed).expect("Failed to decompress compressed data");
112        assert_eq!(status, MZStatus::StreamEnd);
113        assert_eq!(decomp[..], data[..]);
114        assert_eq!(res.bytes_consumed, data.len());
115    }
116}