]>
Commit | Line | Data |
---|---|---|
f035d41b XL |
1 | //! Extra streaming compression functionality. |
2 | //! | |
04454e1e | 3 | //! As of now this is mainly intended for use to build a higher-level wrapper. |
f035d41b XL |
4 | //! |
5 | //! There is no DeflateState as the needed state is contained in the compressor struct itself. | |
f035d41b XL |
6 | |
7 | use crate::deflate::core::{compress, CompressorOxide, TDEFLFlush, TDEFLStatus}; | |
8 | use crate::{MZError, MZFlush, MZStatus, StreamResult}; | |
9 | ||
04454e1e | 10 | /// Try to compress from input to output with the given [`CompressorOxide`]. |
f035d41b XL |
11 | /// |
12 | /// # Errors | |
13 | /// | |
04454e1e FG |
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. | |
f035d41b | 17 | /// |
04454e1e FG |
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. | |
f035d41b XL |
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 | ||
923072b8 FG |
47 | let mut next_in = input; |
48 | let mut next_out = output; | |
f035d41b XL |
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}; | |
04454e1e FG |
105 | use alloc::boxed::Box; |
106 | use alloc::vec; | |
3dfed10e | 107 | |
f035d41b XL |
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 | } |