miniz_oxide/deflate/
stream.rs

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