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 | |
7 | use crate::deflate::core::{compress, CompressorOxide, TDEFLFlush, TDEFLStatus}; |
8 | use 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. |
22 | pub 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)] |
100 | mod 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 | |