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}
122