1use std::cmp;
3use std::fmt;
4use std::marker;
5use std::os::raw::{c_int, c_uint};
6use std::ptr;
7
8use super::*;
9use crate::mem;
10
11#[derive(Clone, Default)]
12pub struct ErrorMessage(Option<&'static str>);
13
14impl ErrorMessage {
15 pub fn get(&self) -> Option<&str> {
16 self.0
17 }
18}
19
20pub struct StreamWrapper {
21 pub inner: *mut mz_stream,
25}
26
27impl fmt::Debug for StreamWrapper {
28 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
29 write!(f, "StreamWrapper")
30 }
31}
32
33impl Default for StreamWrapper {
34 fn default() -> StreamWrapper {
35 StreamWrapper {
40 inner: Box::into_raw(Box::new(mz_stream {
41 next_in: ptr::null_mut(),
42 avail_in: 0,
43 total_in: 0,
44 next_out: ptr::null_mut(),
45 avail_out: 0,
46 total_out: 0,
47 msg: ptr::null_mut(),
48 adler: 0,
49 data_type: 0,
50 reserved: 0,
51 opaque: ptr::null_mut(),
52 state: ptr::null_mut(),
53
54 #[cfg(any(
55 feature = "zlib-ng",
57 all(not(feature = "cloudflare_zlib"), not(feature = "zlib-ng"), not(feature = "zlib-rs"))
59 ))]
60 zalloc: allocator::zalloc,
61 #[cfg(any(
62 feature = "zlib-ng",
64 all(not(feature = "cloudflare_zlib"), not(feature = "zlib-ng"), not(feature = "zlib-rs"))
66 ))]
67 zfree: allocator::zfree,
68
69 #[cfg(
70 all(feature = "cloudflare_zlib", not(feature = "zlib-rs"), not(feature = "zlib-ng")),
72 )]
73 zalloc: Some(allocator::zalloc),
74 #[cfg(
75 all(feature = "cloudflare_zlib", not(feature = "zlib-rs"), not(feature = "zlib-ng")),
77 )]
78 zfree: Some(allocator::zfree),
79
80 #[cfg(all(feature = "zlib-rs", not(feature = "zlib-ng")))]
83 zalloc: None,
84 #[cfg(all(feature = "zlib-rs", not(feature = "zlib-ng")))]
85 zfree: None,
86 })),
87 }
88 }
89}
90
91impl Drop for StreamWrapper {
92 fn drop(&mut self) {
93 drop(unsafe { Box::from_raw(self.inner) });
98 }
99}
100
101#[cfg(any(
102 feature = "zlib-ng",
104 all(feature = "cloudflare_zlib", not(feature = "zlib-rs"), not(feature = "zlib-ng")),
106 all(not(feature = "cloudflare_zlib"), not(feature = "zlib-ng"), not(feature = "zlib-rs")),
108))]
109mod allocator {
110 use super::*;
111
112 use std::alloc::{self, Layout};
113 use std::convert::TryFrom;
114 use std::os::raw::c_void;
115
116 const ALIGN: usize = std::mem::align_of::<usize>();
117
118 fn align_up(size: usize, align: usize) -> usize {
119 (size + align - 1) & !(align - 1)
120 }
121
122 pub extern "C" fn zalloc(_ptr: *mut c_void, items: uInt, item_size: uInt) -> *mut c_void {
123 let size = match items
128 .checked_mul(item_size)
129 .and_then(|i| usize::try_from(i).ok())
130 .map(|size| align_up(size, ALIGN))
131 .and_then(|i| i.checked_add(std::mem::size_of::<usize>()))
132 {
133 Some(i) => i,
134 None => return ptr::null_mut(),
135 };
136
137 let layout = match Layout::from_size_align(size, ALIGN) {
139 Ok(layout) => layout,
140 Err(_) => return ptr::null_mut(),
141 };
142
143 unsafe {
144 let ptr = alloc::alloc(layout) as *mut usize;
147 if ptr.is_null() {
148 return ptr as *mut c_void;
149 }
150 *ptr = size;
151 ptr.add(1) as *mut c_void
152 }
153 }
154
155 pub extern "C" fn zfree(_ptr: *mut c_void, address: *mut c_void) {
156 unsafe {
157 let ptr = (address as *mut usize).offset(-1);
161 let size = *ptr;
162 let layout = Layout::from_size_align_unchecked(size, ALIGN);
163 alloc::dealloc(ptr as *mut u8, layout)
164 }
165 }
166}
167
168unsafe impl<D: Direction> Send for Stream<D> {}
169unsafe impl<D: Direction> Sync for Stream<D> {}
170
171pub trait Direction {
174 unsafe fn destroy(stream: *mut mz_stream) -> c_int;
175}
176
177#[derive(Debug)]
178pub enum DirCompress {}
179#[derive(Debug)]
180pub enum DirDecompress {}
181
182#[derive(Debug)]
183pub struct Stream<D: Direction> {
184 pub stream_wrapper: StreamWrapper,
185 pub total_in: u64,
186 pub total_out: u64,
187 pub _marker: marker::PhantomData<D>,
188}
189
190impl<D: Direction> Stream<D> {
191 pub fn msg(&self) -> ErrorMessage {
192 let msg = unsafe { (*self.stream_wrapper.inner).msg };
196 ErrorMessage(if msg.is_null() {
197 None
198 } else {
199 let s = unsafe { std::ffi::CStr::from_ptr(msg) };
200 std::str::from_utf8(s.to_bytes()).ok()
201 })
202 }
203}
204
205impl<D: Direction> Drop for Stream<D> {
206 fn drop(&mut self) {
207 unsafe {
208 let _ = D::destroy(self.stream_wrapper.inner);
209 }
210 }
211}
212
213impl Direction for DirCompress {
214 unsafe fn destroy(stream: *mut mz_stream) -> c_int {
215 mz_deflateEnd(stream)
216 }
217}
218impl Direction for DirDecompress {
219 unsafe fn destroy(stream: *mut mz_stream) -> c_int {
220 mz_inflateEnd(stream)
221 }
222}
223
224#[derive(Debug)]
225pub struct Inflate {
226 pub inner: Stream<DirDecompress>,
227}
228
229impl InflateBackend for Inflate {
230 fn make(zlib_header: bool, window_bits: u8) -> Self {
231 unsafe {
232 let state = StreamWrapper::default();
233 let ret = mz_inflateInit2(
234 state.inner,
235 if zlib_header {
236 window_bits as c_int
237 } else {
238 -(window_bits as c_int)
239 },
240 );
241 assert_eq!(ret, 0);
242 Inflate {
243 inner: Stream {
244 stream_wrapper: state,
245 total_in: 0,
246 total_out: 0,
247 _marker: marker::PhantomData,
248 },
249 }
250 }
251 }
252
253 fn decompress(
254 &mut self,
255 input: &[u8],
256 output: &mut [u8],
257 flush: FlushDecompress,
258 ) -> Result<Status, DecompressError> {
259 let raw = self.inner.stream_wrapper.inner;
260 unsafe {
264 (*raw).msg = ptr::null_mut();
265 (*raw).next_in = input.as_ptr() as *mut u8;
266 (*raw).avail_in = cmp::min(input.len(), c_uint::MAX as usize) as c_uint;
267 (*raw).next_out = output.as_mut_ptr();
268 (*raw).avail_out = cmp::min(output.len(), c_uint::MAX as usize) as c_uint;
269
270 let rc = mz_inflate(raw, flush as c_int);
271
272 self.inner.total_in += ((*raw).next_in as usize - input.as_ptr() as usize) as u64;
275 self.inner.total_out += ((*raw).next_out as usize - output.as_ptr() as usize) as u64;
276
277 (*raw).next_in = ptr::null_mut();
279 (*raw).avail_in = 0;
280 (*raw).next_out = ptr::null_mut();
281 (*raw).avail_out = 0;
282
283 match rc {
284 MZ_DATA_ERROR | MZ_STREAM_ERROR | MZ_MEM_ERROR => {
285 mem::decompress_failed(self.inner.msg())
286 }
287 MZ_OK => Ok(Status::Ok),
288 MZ_BUF_ERROR => Ok(Status::BufError),
289 MZ_STREAM_END => Ok(Status::StreamEnd),
290 MZ_NEED_DICT => mem::decompress_need_dict((*raw).adler as u32),
291 c => panic!("unknown return code: {}", c),
292 }
293 }
294 }
295
296 fn reset(&mut self, zlib_header: bool) {
297 let bits = if zlib_header {
298 MZ_DEFAULT_WINDOW_BITS
299 } else {
300 -MZ_DEFAULT_WINDOW_BITS
301 };
302 unsafe {
303 inflateReset2(self.inner.stream_wrapper.inner, bits);
304 }
305 self.inner.total_out = 0;
306 self.inner.total_in = 0;
307 }
308}
309
310impl Backend for Inflate {
311 #[inline]
312 fn total_in(&self) -> u64 {
313 self.inner.total_in
314 }
315
316 #[inline]
317 fn total_out(&self) -> u64 {
318 self.inner.total_out
319 }
320}
321
322#[derive(Debug)]
323pub struct Deflate {
324 pub inner: Stream<DirCompress>,
325}
326
327impl DeflateBackend for Deflate {
328 fn make(level: Compression, zlib_header: bool, window_bits: u8) -> Self {
329 unsafe {
330 let state = StreamWrapper::default();
331 let ret = mz_deflateInit2(
332 state.inner,
333 level.0 as c_int,
334 MZ_DEFLATED,
335 if zlib_header {
336 window_bits as c_int
337 } else {
338 -(window_bits as c_int)
339 },
340 8,
341 MZ_DEFAULT_STRATEGY,
342 );
343 assert_eq!(ret, 0);
344 Deflate {
345 inner: Stream {
346 stream_wrapper: state,
347 total_in: 0,
348 total_out: 0,
349 _marker: marker::PhantomData,
350 },
351 }
352 }
353 }
354 fn compress(
355 &mut self,
356 input: &[u8],
357 output: &mut [u8],
358 flush: FlushCompress,
359 ) -> Result<Status, CompressError> {
360 let raw = self.inner.stream_wrapper.inner;
361 unsafe {
365 (*raw).msg = ptr::null_mut();
366 (*raw).next_in = input.as_ptr() as *mut _;
367 (*raw).avail_in = cmp::min(input.len(), c_uint::MAX as usize) as c_uint;
368 (*raw).next_out = output.as_mut_ptr();
369 (*raw).avail_out = cmp::min(output.len(), c_uint::MAX as usize) as c_uint;
370
371 let rc = mz_deflate(raw, flush as c_int);
372
373 self.inner.total_in += ((*raw).next_in as usize - input.as_ptr() as usize) as u64;
377 self.inner.total_out += ((*raw).next_out as usize - output.as_ptr() as usize) as u64;
378 (*raw).next_in = ptr::null_mut();
380 (*raw).avail_in = 0;
381 (*raw).next_out = ptr::null_mut();
382 (*raw).avail_out = 0;
383
384 match rc {
385 MZ_OK => Ok(Status::Ok),
386 MZ_BUF_ERROR => Ok(Status::BufError),
387 MZ_STREAM_END => Ok(Status::StreamEnd),
388 MZ_STREAM_ERROR => mem::compress_failed(self.inner.msg()),
389 c => panic!("unknown return code: {}", c),
390 }
391 }
392 }
393
394 fn reset(&mut self) {
395 self.inner.total_in = 0;
396 self.inner.total_out = 0;
397 let rc = unsafe { mz_deflateReset(self.inner.stream_wrapper.inner) };
398 assert_eq!(rc, MZ_OK);
399 }
400}
401
402impl Backend for Deflate {
403 #[inline]
404 fn total_in(&self) -> u64 {
405 self.inner.total_in
406 }
407
408 #[inline]
409 fn total_out(&self) -> u64 {
410 self.inner.total_out
411 }
412}
413
414pub use self::c_backend::*;
415
416#[allow(bad_style)]
418#[allow(unused_imports)]
419mod c_backend {
420 use std::mem;
421 use std::os::raw::{c_char, c_int};
422
423 #[cfg(feature = "zlib-ng")]
424 use libz_ng_sys as libz;
425
426 #[cfg(all(feature = "zlib-rs", not(feature = "zlib-ng")))]
427 use libz_rs_sys as libz;
428
429 #[cfg(
430 all(feature = "cloudflare_zlib", not(feature = "zlib-rs"), not(feature = "zlib-ng")),
432 )]
433 use cloudflare_zlib_sys as libz;
434
435 #[cfg(
436 all(not(feature = "cloudflare_zlib"), not(feature = "zlib-ng"), not(feature = "zlib-rs")),
438 )]
439 use libz_sys as libz;
440
441 pub use libz::deflate as mz_deflate;
442 pub use libz::deflateEnd as mz_deflateEnd;
443 pub use libz::deflateReset as mz_deflateReset;
444 pub use libz::inflate as mz_inflate;
445 pub use libz::inflateEnd as mz_inflateEnd;
446 pub use libz::z_stream as mz_stream;
447 pub use libz::*;
448
449 pub use libz::Z_BLOCK as MZ_BLOCK;
450 pub use libz::Z_BUF_ERROR as MZ_BUF_ERROR;
451 pub use libz::Z_DATA_ERROR as MZ_DATA_ERROR;
452 pub use libz::Z_DEFAULT_STRATEGY as MZ_DEFAULT_STRATEGY;
453 pub use libz::Z_DEFLATED as MZ_DEFLATED;
454 pub use libz::Z_FINISH as MZ_FINISH;
455 pub use libz::Z_FULL_FLUSH as MZ_FULL_FLUSH;
456 pub use libz::Z_MEM_ERROR as MZ_MEM_ERROR;
457 pub use libz::Z_NEED_DICT as MZ_NEED_DICT;
458 pub use libz::Z_NO_FLUSH as MZ_NO_FLUSH;
459 pub use libz::Z_OK as MZ_OK;
460 pub use libz::Z_PARTIAL_FLUSH as MZ_PARTIAL_FLUSH;
461 pub use libz::Z_STREAM_END as MZ_STREAM_END;
462 pub use libz::Z_STREAM_ERROR as MZ_STREAM_ERROR;
463 pub use libz::Z_SYNC_FLUSH as MZ_SYNC_FLUSH;
464
465 pub const MZ_DEFAULT_WINDOW_BITS: c_int = 15;
466
467 #[cfg(feature = "zlib-ng")]
468 const ZLIB_VERSION: &'static str = "2.1.0.devel\0";
469 #[cfg(all(not(feature = "zlib-ng"), feature = "zlib-rs"))]
470 const ZLIB_VERSION: &'static str = "1.3.0-zlib-rs-0.4.2\0";
471 #[cfg(not(any(feature = "zlib-ng", feature = "zlib-rs")))]
472 const ZLIB_VERSION: &'static str = "1.2.8\0";
473
474 pub unsafe extern "C" fn mz_deflateInit2(
475 stream: *mut mz_stream,
476 level: c_int,
477 method: c_int,
478 window_bits: c_int,
479 mem_level: c_int,
480 strategy: c_int,
481 ) -> c_int {
482 libz::deflateInit2_(
483 stream,
484 level,
485 method,
486 window_bits,
487 mem_level,
488 strategy,
489 ZLIB_VERSION.as_ptr() as *const c_char,
490 mem::size_of::<mz_stream>() as c_int,
491 )
492 }
493 pub unsafe extern "C" fn mz_inflateInit2(stream: *mut mz_stream, window_bits: c_int) -> c_int {
494 libz::inflateInit2_(
495 stream,
496 window_bits,
497 ZLIB_VERSION.as_ptr() as *const c_char,
498 mem::size_of::<mz_stream>() as c_int,
499 )
500 }
501}