1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
use core::ffi::c_uint;
use crate::deflate::DeflateConfig;
use crate::inflate::InflateConfig;
use crate::ReturnCode;
pub use crate::{DeflateFlush, InflateFlush};
/// Possible status results of compressing some data or successfully
/// decompressing a block of data.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Status {
/// Indicates success.
///
/// Means that more input may be needed but isn't available
/// and/or there's more output to be written but the output buffer is full.
Ok,
/// Indicates that forward progress is not possible due to input or output
/// buffers being empty.
///
/// For compression it means the input buffer needs some more data or the
/// output buffer needs to be freed up before trying again.
///
/// For decompression this means that more input is needed to continue or
/// the output buffer isn't large enough to contain the result. The function
/// can be called again after fixing both.
BufError,
/// Indicates that all input has been consumed and all output bytes have
/// been written. Decompression/compression should not be called again.
///
/// For decompression with zlib streams the adler-32 of the decompressed
/// data has also been verified.
StreamEnd,
}
/// Errors that can occur when decompressing.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[repr(i32)]
pub enum InflateError {
/// Decompressing this input requires a dictionary.
NeedDict { dict_id: u32 } = 2,
/// The [`Inflate`] is in an inconsistent state, most likely
/// due to an invalid configuration parameter.
StreamError = -2,
/// The input is not a valid deflate stream.
DataError = -3,
/// A memory allocation failed.
MemError = -4,
}
impl From<InflateError> for ReturnCode {
fn from(value: InflateError) -> Self {
match value {
InflateError::NeedDict { .. } => ReturnCode::NeedDict,
InflateError::StreamError => ReturnCode::StreamError,
InflateError::DataError => ReturnCode::DataError,
InflateError::MemError => ReturnCode::MemError,
}
}
}
impl InflateError {
pub fn as_str(self) -> &'static str {
ReturnCode::from(self).error_message_str()
}
}
/// The state that is used to decompress an input.
pub struct Inflate {
inner: crate::inflate::InflateStream<'static>,
total_in: u64,
total_out: u64,
}
impl Inflate {
/// The amount of bytes consumed from the input so far.
pub fn total_in(&self) -> u64 {
self.total_in
}
/// The amount of decompressed bytes that have been written to the output thus far.
pub fn total_out(&self) -> u64 {
self.total_out
}
/// The error message if the previous operation failed.
pub fn error_message(&self) -> Option<&'static str> {
if self.inner.msg.is_null() {
None
} else {
unsafe { core::ffi::CStr::from_ptr(self.inner.msg).to_str() }.ok()
}
}
/// Create a new instance. Note that it allocates in various ways and thus should be re-used.
///
/// The `window_bits` must be in the range `8..=15`, with `15` being most common.
pub fn new(zlib_header: bool, window_bits: u8) -> Self {
let config = InflateConfig {
window_bits: if zlib_header {
i32::from(window_bits)
} else {
-i32::from(window_bits)
},
};
Self {
inner: crate::inflate::InflateStream::new(config),
total_in: 0,
total_out: 0,
}
}
/// Reset the state to allow handling a new stream.
pub fn reset(&mut self, zlib_header: bool) {
let mut config = InflateConfig::default();
if !zlib_header {
config.window_bits = -config.window_bits;
}
self.total_in = 0;
self.total_out = 0;
crate::inflate::reset_with_config(&mut self.inner, config);
}
/// Decompress `input` and write all decompressed bytes into `output`, with `flush` defining some details about this.
pub fn decompress(
&mut self,
input: &[u8],
output: &mut [u8],
flush: InflateFlush,
) -> Result<Status, InflateError> {
// Limit the length of the input and output to the maximum value of a c_uint. For larger
// inputs, this will either complete or signal that more input and output is needed. The
// caller should be able to handle this regardless.
self.inner.avail_in = Ord::min(input.len(), c_uint::MAX as usize) as c_uint;
self.inner.avail_out = Ord::min(output.len(), c_uint::MAX as usize) as c_uint;
// This cast_mut is unfortunate, that is just how the types are.
self.inner.next_in = input.as_ptr().cast_mut();
self.inner.next_out = output.as_mut_ptr();
let start_in = self.inner.next_in;
let start_out = self.inner.next_out;
// SAFETY: the inflate state was properly initialized.
let ret = unsafe { crate::inflate::inflate(&mut self.inner, flush) };
self.total_in += (self.inner.next_in as usize - start_in as usize) as u64;
self.total_out += (self.inner.next_out as usize - start_out as usize) as u64;
match ret {
ReturnCode::Ok => Ok(Status::Ok),
ReturnCode::StreamEnd => Ok(Status::StreamEnd),
ReturnCode::NeedDict => Err(InflateError::NeedDict {
dict_id: self.inner.adler as u32,
}),
ReturnCode::ErrNo => unreachable!("the rust API does not use files"),
ReturnCode::StreamError => Err(InflateError::StreamError),
ReturnCode::DataError => Err(InflateError::DataError),
ReturnCode::MemError => Err(InflateError::MemError),
ReturnCode::BufError => Ok(Status::BufError),
ReturnCode::VersionError => unreachable!("the rust API does not use the version"),
}
}
pub fn set_dictionary(&mut self, dictionary: &[u8]) -> Result<u32, InflateError> {
match crate::inflate::set_dictionary(&mut self.inner, dictionary) {
ReturnCode::Ok => Ok(self.inner.adler as u32),
ReturnCode::StreamError => Err(InflateError::StreamError),
ReturnCode::DataError => Err(InflateError::DataError),
other => unreachable!("set_dictionary does not return {other:?}"),
}
}
}
impl Drop for Inflate {
fn drop(&mut self) {
let _ = crate::inflate::end(&mut self.inner);
}
}
/// Errors that can occur when compressing.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum DeflateError {
/// The [`Deflate`] is in an inconsistent state, most likely
/// due to an invalid configuration parameter.
StreamError = -2,
/// The input is not a valid deflate stream.
DataError = -3,
/// A memory allocation failed.
MemError = -4,
}
impl From<DeflateError> for ReturnCode {
fn from(value: DeflateError) -> Self {
match value {
DeflateError::StreamError => ReturnCode::StreamError,
DeflateError::DataError => ReturnCode::DataError,
DeflateError::MemError => ReturnCode::MemError,
}
}
}
impl DeflateError {
pub fn as_str(self) -> &'static str {
ReturnCode::from(self).error_message_str()
}
}
impl From<ReturnCode> for Result<Status, DeflateError> {
fn from(value: ReturnCode) -> Self {
match value {
ReturnCode::Ok => Ok(Status::Ok),
ReturnCode::StreamEnd => Ok(Status::StreamEnd),
ReturnCode::NeedDict => unreachable!("compression does not use dictionary"),
ReturnCode::ErrNo => unreachable!("the rust API does not use files"),
ReturnCode::StreamError => Err(DeflateError::StreamError),
ReturnCode::DataError => Err(DeflateError::DataError),
ReturnCode::MemError => Err(DeflateError::MemError),
ReturnCode::BufError => Ok(Status::BufError),
ReturnCode::VersionError => unreachable!("the rust API does not use the version"),
}
}
}
/// The state that is used to compress an input.
pub struct Deflate {
inner: crate::deflate::DeflateStream<'static>,
total_in: u64,
total_out: u64,
}
impl Deflate {
/// The number of bytes that were read from the input.
pub fn total_in(&self) -> u64 {
self.total_in
}
/// The number of compressed bytes that were written to the output.
pub fn total_out(&self) -> u64 {
self.total_out
}
/// The error message if the previous operation failed.
pub fn error_message(&self) -> Option<&'static str> {
if self.inner.msg.is_null() {
None
} else {
unsafe { core::ffi::CStr::from_ptr(self.inner.msg).to_str() }.ok()
}
}
/// Create a new instance - this allocates so should be done with care.
///
/// The `window_bits` must be in the range `8..=15`, with `15` being most common.
pub fn new(level: i32, zlib_header: bool, window_bits: u8) -> Self {
let config = DeflateConfig {
window_bits: if zlib_header {
i32::from(window_bits)
} else {
-i32::from(window_bits)
},
level,
..DeflateConfig::default()
};
Self {
inner: crate::deflate::DeflateStream::new(config),
total_in: 0,
total_out: 0,
}
}
/// Prepare the instance for a new stream.
pub fn reset(&mut self) {
self.total_in = 0;
self.total_out = 0;
crate::deflate::reset(&mut self.inner);
}
/// Compress `input` and write compressed bytes to `output`, with `flush` controlling additional characteristics.
pub fn compress(
&mut self,
input: &[u8],
output: &mut [u8],
flush: DeflateFlush,
) -> Result<Status, DeflateError> {
// Limit the length of the input and output to the maximum value of a c_uint. For larger
// inputs, this will either complete or signal that more input and output is needed. The
// caller should be able to handle this regardless.
self.inner.avail_in = Ord::min(input.len(), c_uint::MAX as usize) as c_uint;
self.inner.avail_out = Ord::min(output.len(), c_uint::MAX as usize) as c_uint;
// This cast_mut is unfortunate, that is just how the types are.
self.inner.next_in = input.as_ptr().cast_mut();
self.inner.next_out = output.as_mut_ptr();
let start_in = self.inner.next_in;
let start_out = self.inner.next_out;
let ret = crate::deflate::deflate(&mut self.inner, flush).into();
self.total_in += (self.inner.next_in as usize - start_in as usize) as u64;
self.total_out += (self.inner.next_out as usize - start_out as usize) as u64;
ret
}
/// Specifies the compression dictionary to use.
///
/// Returns the Adler-32 checksum of the dictionary.
pub fn set_dictionary(&mut self, dictionary: &[u8]) -> Result<u32, DeflateError> {
match crate::deflate::set_dictionary(&mut self.inner, dictionary) {
ReturnCode::Ok => Ok(self.inner.adler as u32),
ReturnCode::StreamError => Err(DeflateError::StreamError),
other => unreachable!("set_dictionary does not return {other:?}"),
}
}
/// Dynamically updates the compression level.
///
/// This can be used to switch between compression levels for different
/// kinds of data, or it can be used in conjunction with a call to [`Deflate::reset`]
/// to reuse the compressor.
///
/// This may return an error if there wasn't enough output space to complete
/// the compression of the available input data before changing the
/// compression level. Flushing the stream before calling this method
/// ensures that the function will succeed on the first call.
pub fn set_level(&mut self, level: i32) -> Result<Status, DeflateError> {
match crate::deflate::params(&mut self.inner, level, Default::default()) {
ReturnCode::Ok => Ok(Status::Ok),
ReturnCode::StreamError => Err(DeflateError::StreamError),
ReturnCode::BufError => Ok(Status::BufError),
other => unreachable!("set_level does not return {other:?}"),
}
}
}
impl Drop for Deflate {
fn drop(&mut self) {
let _ = crate::deflate::end(&mut self.inner);
}
}