1 //! Implementation for C backends.
2 use std::alloc::{self, Layout};
3 use std::cmp;
4 use std::convert::TryFrom;
5 use std::fmt;
6 use std::marker;
7 use std::ops::{Deref, DerefMut};
8 use std::ptr;
9 
10 pub use libc::{c_int, c_uint, c_void, size_t};
11 
12 use super::*;
13 use crate::mem::{self, FlushDecompress, Status};
14 
15 pub struct StreamWrapper {
16     pub inner: Box<mz_stream>,
17 }
18 
19 impl fmt::Debug for StreamWrapper {
fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>20     fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
21         write!(f, "StreamWrapper")
22     }
23 }
24 
25 impl Default for StreamWrapper {
default() -> StreamWrapper26     fn default() -> StreamWrapper {
27         StreamWrapper {
28             inner: Box::new(mz_stream {
29                 next_in: ptr::null_mut(),
30                 avail_in: 0,
31                 total_in: 0,
32                 next_out: ptr::null_mut(),
33                 avail_out: 0,
34                 total_out: 0,
35                 msg: ptr::null_mut(),
36                 adler: 0,
37                 data_type: 0,
38                 reserved: 0,
39                 opaque: ptr::null_mut(),
40                 state: ptr::null_mut(),
41                 #[cfg(feature = "any_zlib")]
42                 zalloc,
43                 #[cfg(feature = "any_zlib")]
44                 zfree,
45                 #[cfg(not(feature = "any_zlib"))]
46                 zalloc: Some(zalloc),
47                 #[cfg(not(feature = "any_zlib"))]
48                 zfree: Some(zfree),
49             }),
50         }
51     }
52 }
53 
54 const ALIGN: usize = std::mem::align_of::<usize>();
55 
align_up(size: usize, align: usize) -> usize56 fn align_up(size: usize, align: usize) -> usize {
57     (size + align - 1) & !(align - 1)
58 }
59 
zalloc(_ptr: *mut c_void, items: AllocSize, item_size: AllocSize) -> *mut c_void60 extern "C" fn zalloc(_ptr: *mut c_void, items: AllocSize, item_size: AllocSize) -> *mut c_void {
61     // We need to multiply `items` and `item_size` to get the actual desired
62     // allocation size. Since `zfree` doesn't receive a size argument we
63     // also need to allocate space for a `usize` as a header so we can store
64     // how large the allocation is to deallocate later.
65     let size = match items
66         .checked_mul(item_size)
67         .and_then(|i| usize::try_from(i).ok())
68         .map(|size| align_up(size, ALIGN))
69         .and_then(|i| i.checked_add(std::mem::size_of::<usize>()))
70     {
71         Some(i) => i,
72         None => return ptr::null_mut(),
73     };
74 
75     // Make sure the `size` isn't too big to fail `Layout`'s restrictions
76     let layout = match Layout::from_size_align(size, ALIGN) {
77         Ok(layout) => layout,
78         Err(_) => return ptr::null_mut(),
79     };
80 
81     unsafe {
82         // Allocate the data, and if successful store the size we allocated
83         // at the beginning and then return an offset pointer.
84         let ptr = alloc::alloc(layout) as *mut usize;
85         if ptr.is_null() {
86             return ptr as *mut c_void;
87         }
88         *ptr = size;
89         ptr.add(1) as *mut c_void
90     }
91 }
92 
zfree(_ptr: *mut c_void, address: *mut c_void)93 extern "C" fn zfree(_ptr: *mut c_void, address: *mut c_void) {
94     unsafe {
95         // Move our address being free'd back one pointer, read the size we
96         // stored in `zalloc`, and then free it using the standard Rust
97         // allocator.
98         let ptr = (address as *mut usize).offset(-1);
99         let size = *ptr;
100         let layout = Layout::from_size_align_unchecked(size, ALIGN);
101         alloc::dealloc(ptr as *mut u8, layout)
102     }
103 }
104 
105 impl Deref for StreamWrapper {
106     type Target = mz_stream;
107 
deref(&self) -> &Self::Target108     fn deref(&self) -> &Self::Target {
109         &*self.inner
110     }
111 }
112 
113 impl DerefMut for StreamWrapper {
deref_mut(&mut self) -> &mut Self::Target114     fn deref_mut(&mut self) -> &mut Self::Target {
115         &mut *self.inner
116     }
117 }
118 
119 unsafe impl<D: Direction> Send for Stream<D> {}
120 unsafe impl<D: Direction> Sync for Stream<D> {}
121 
122 /// Trait used to call the right destroy/end function on the inner
123 /// stream object on drop.
124 pub trait Direction {
destroy(stream: *mut mz_stream) -> c_int125     unsafe fn destroy(stream: *mut mz_stream) -> c_int;
126 }
127 
128 #[derive(Debug)]
129 pub enum DirCompress {}
130 #[derive(Debug)]
131 pub enum DirDecompress {}
132 
133 #[derive(Debug)]
134 pub struct Stream<D: Direction> {
135     pub stream_wrapper: StreamWrapper,
136     pub total_in: u64,
137     pub total_out: u64,
138     pub _marker: marker::PhantomData<D>,
139 }
140 
141 impl<D: Direction> Drop for Stream<D> {
drop(&mut self)142     fn drop(&mut self) {
143         unsafe {
144             let _ = D::destroy(&mut *self.stream_wrapper);
145         }
146     }
147 }
148 
149 impl Direction for DirCompress {
destroy(stream: *mut mz_stream) -> c_int150     unsafe fn destroy(stream: *mut mz_stream) -> c_int {
151         mz_deflateEnd(stream)
152     }
153 }
154 impl Direction for DirDecompress {
destroy(stream: *mut mz_stream) -> c_int155     unsafe fn destroy(stream: *mut mz_stream) -> c_int {
156         mz_inflateEnd(stream)
157     }
158 }
159 
160 #[derive(Debug)]
161 pub struct Inflate {
162     pub inner: Stream<DirDecompress>,
163 }
164 
165 impl InflateBackend for Inflate {
make(zlib_header: bool, window_bits: u8) -> Self166     fn make(zlib_header: bool, window_bits: u8) -> Self {
167         unsafe {
168             let mut state = StreamWrapper::default();
169             let ret = mz_inflateInit2(
170                 &mut *state,
171                 if zlib_header {
172                     window_bits as c_int
173                 } else {
174                     -(window_bits as c_int)
175                 },
176             );
177             assert_eq!(ret, 0);
178             Inflate {
179                 inner: Stream {
180                     stream_wrapper: state,
181                     total_in: 0,
182                     total_out: 0,
183                     _marker: marker::PhantomData,
184                 },
185             }
186         }
187     }
188 
decompress( &mut self, input: &[u8], output: &mut [u8], flush: FlushDecompress, ) -> Result<Status, DecompressError>189     fn decompress(
190         &mut self,
191         input: &[u8],
192         output: &mut [u8],
193         flush: FlushDecompress,
194     ) -> Result<Status, DecompressError> {
195         let raw = &mut *self.inner.stream_wrapper;
196         raw.next_in = input.as_ptr() as *mut u8;
197         raw.avail_in = cmp::min(input.len(), c_uint::max_value() as usize) as c_uint;
198         raw.next_out = output.as_mut_ptr();
199         raw.avail_out = cmp::min(output.len(), c_uint::max_value() as usize) as c_uint;
200 
201         let rc = unsafe { mz_inflate(raw, flush as c_int) };
202 
203         // Unfortunately the total counters provided by zlib might be only
204         // 32 bits wide and overflow while processing large amounts of data.
205         self.inner.total_in += (raw.next_in as usize - input.as_ptr() as usize) as u64;
206         self.inner.total_out += (raw.next_out as usize - output.as_ptr() as usize) as u64;
207 
208         match rc {
209             MZ_DATA_ERROR | MZ_STREAM_ERROR => mem::decompress_failed(),
210             MZ_OK => Ok(Status::Ok),
211             MZ_BUF_ERROR => Ok(Status::BufError),
212             MZ_STREAM_END => Ok(Status::StreamEnd),
213             MZ_NEED_DICT => mem::decompress_need_dict(raw.adler as u32),
214             c => panic!("unknown return code: {}", c),
215         }
216     }
217 
218     #[cfg(feature = "any_zlib")]
reset(&mut self, zlib_header: bool)219     fn reset(&mut self, zlib_header: bool) {
220         let bits = if zlib_header {
221             MZ_DEFAULT_WINDOW_BITS
222         } else {
223             -MZ_DEFAULT_WINDOW_BITS
224         };
225         unsafe {
226             inflateReset2(&mut *self.inner.stream_wrapper, bits);
227         }
228         self.inner.total_out = 0;
229         self.inner.total_in = 0;
230     }
231 
232     #[cfg(not(feature = "any_zlib"))]
reset(&mut self, zlib_header: bool)233     fn reset(&mut self, zlib_header: bool) {
234         *self = Self::make(zlib_header, MZ_DEFAULT_WINDOW_BITS as u8);
235     }
236 }
237 
238 impl Backend for Inflate {
239     #[inline]
total_in(&self) -> u64240     fn total_in(&self) -> u64 {
241         self.inner.total_in
242     }
243 
244     #[inline]
total_out(&self) -> u64245     fn total_out(&self) -> u64 {
246         self.inner.total_out
247     }
248 }
249 
250 #[derive(Debug)]
251 pub struct Deflate {
252     pub inner: Stream<DirCompress>,
253 }
254 
255 impl DeflateBackend for Deflate {
make(level: Compression, zlib_header: bool, window_bits: u8) -> Self256     fn make(level: Compression, zlib_header: bool, window_bits: u8) -> Self {
257         unsafe {
258             let mut state = StreamWrapper::default();
259             let ret = mz_deflateInit2(
260                 &mut *state,
261                 level.0 as c_int,
262                 MZ_DEFLATED,
263                 if zlib_header {
264                     window_bits as c_int
265                 } else {
266                     -(window_bits as c_int)
267                 },
268                 8,
269                 MZ_DEFAULT_STRATEGY,
270             );
271             assert_eq!(ret, 0);
272             Deflate {
273                 inner: Stream {
274                     stream_wrapper: state,
275                     total_in: 0,
276                     total_out: 0,
277                     _marker: marker::PhantomData,
278                 },
279             }
280         }
281     }
compress( &mut self, input: &[u8], output: &mut [u8], flush: FlushCompress, ) -> Result<Status, CompressError>282     fn compress(
283         &mut self,
284         input: &[u8],
285         output: &mut [u8],
286         flush: FlushCompress,
287     ) -> Result<Status, CompressError> {
288         let raw = &mut *self.inner.stream_wrapper;
289         raw.next_in = input.as_ptr() as *mut _;
290         raw.avail_in = cmp::min(input.len(), c_uint::max_value() as usize) as c_uint;
291         raw.next_out = output.as_mut_ptr();
292         raw.avail_out = cmp::min(output.len(), c_uint::max_value() as usize) as c_uint;
293 
294         let rc = unsafe { mz_deflate(raw, flush as c_int) };
295 
296         // Unfortunately the total counters provided by zlib might be only
297         // 32 bits wide and overflow while processing large amounts of data.
298         self.inner.total_in += (raw.next_in as usize - input.as_ptr() as usize) as u64;
299         self.inner.total_out += (raw.next_out as usize - output.as_ptr() as usize) as u64;
300 
301         match rc {
302             MZ_OK => Ok(Status::Ok),
303             MZ_BUF_ERROR => Ok(Status::BufError),
304             MZ_STREAM_END => Ok(Status::StreamEnd),
305             MZ_STREAM_ERROR => Err(CompressError(())),
306             c => panic!("unknown return code: {}", c),
307         }
308     }
309 
reset(&mut self)310     fn reset(&mut self) {
311         self.inner.total_in = 0;
312         self.inner.total_out = 0;
313         let rc = unsafe { mz_deflateReset(&mut *self.inner.stream_wrapper) };
314         assert_eq!(rc, MZ_OK);
315     }
316 }
317 
318 impl Backend for Deflate {
319     #[inline]
total_in(&self) -> u64320     fn total_in(&self) -> u64 {
321         self.inner.total_in
322     }
323 
324     #[inline]
total_out(&self) -> u64325     fn total_out(&self) -> u64 {
326         self.inner.total_out
327     }
328 }
329 
330 pub use self::c_backend::*;
331 
332 /// Miniz specific
333 #[cfg(not(feature = "any_zlib"))]
334 mod c_backend {
335     pub use miniz_sys::*;
336     pub type AllocSize = libc::size_t;
337 }
338 
339 /// Zlib specific
340 #[cfg(any(
341     feature = "zlib-ng-compat",
342     all(feature = "zlib", not(feature = "cloudflare_zlib"))
343 ))]
344 #[allow(bad_style)]
345 mod c_backend {
346     use libc::{c_char, c_int};
347     use std::mem;
348 
349     pub use libz_sys::deflate as mz_deflate;
350     pub use libz_sys::deflateEnd as mz_deflateEnd;
351     pub use libz_sys::deflateReset as mz_deflateReset;
352     pub use libz_sys::inflate as mz_inflate;
353     pub use libz_sys::inflateEnd as mz_inflateEnd;
354     pub use libz_sys::z_stream as mz_stream;
355     pub use libz_sys::*;
356 
357     pub use libz_sys::Z_BLOCK as MZ_BLOCK;
358     pub use libz_sys::Z_BUF_ERROR as MZ_BUF_ERROR;
359     pub use libz_sys::Z_DATA_ERROR as MZ_DATA_ERROR;
360     pub use libz_sys::Z_DEFAULT_STRATEGY as MZ_DEFAULT_STRATEGY;
361     pub use libz_sys::Z_DEFLATED as MZ_DEFLATED;
362     pub use libz_sys::Z_FINISH as MZ_FINISH;
363     pub use libz_sys::Z_FULL_FLUSH as MZ_FULL_FLUSH;
364     pub use libz_sys::Z_NEED_DICT as MZ_NEED_DICT;
365     pub use libz_sys::Z_NO_FLUSH as MZ_NO_FLUSH;
366     pub use libz_sys::Z_OK as MZ_OK;
367     pub use libz_sys::Z_PARTIAL_FLUSH as MZ_PARTIAL_FLUSH;
368     pub use libz_sys::Z_STREAM_END as MZ_STREAM_END;
369     pub use libz_sys::Z_STREAM_ERROR as MZ_STREAM_ERROR;
370     pub use libz_sys::Z_SYNC_FLUSH as MZ_SYNC_FLUSH;
371     pub type AllocSize = libz_sys::uInt;
372 
373     pub const MZ_DEFAULT_WINDOW_BITS: c_int = 15;
374 
375     const ZLIB_VERSION: &'static str = "1.2.8\0";
376 
mz_deflateInit2( stream: *mut mz_stream, level: c_int, method: c_int, window_bits: c_int, mem_level: c_int, strategy: c_int, ) -> c_int377     pub unsafe extern "C" fn mz_deflateInit2(
378         stream: *mut mz_stream,
379         level: c_int,
380         method: c_int,
381         window_bits: c_int,
382         mem_level: c_int,
383         strategy: c_int,
384     ) -> c_int {
385         libz_sys::deflateInit2_(
386             stream,
387             level,
388             method,
389             window_bits,
390             mem_level,
391             strategy,
392             ZLIB_VERSION.as_ptr() as *const c_char,
393             mem::size_of::<mz_stream>() as c_int,
394         )
395     }
mz_inflateInit2(stream: *mut mz_stream, window_bits: c_int) -> c_int396     pub unsafe extern "C" fn mz_inflateInit2(stream: *mut mz_stream, window_bits: c_int) -> c_int {
397         libz_sys::inflateInit2_(
398             stream,
399             window_bits,
400             ZLIB_VERSION.as_ptr() as *const c_char,
401             mem::size_of::<mz_stream>() as c_int,
402         )
403     }
404 }
405 
406 /// Cloudflare optimized Zlib specific
407 #[cfg(all(feature = "cloudflare_zlib", not(feature = "zlib-ng-compat")))]
408 #[allow(bad_style)]
409 mod c_backend {
410     use libc::{c_char, c_int};
411     use std::mem;
412 
413     pub use cloudflare_zlib_sys::deflate as mz_deflate;
414     pub use cloudflare_zlib_sys::deflateEnd as mz_deflateEnd;
415     pub use cloudflare_zlib_sys::deflateReset as mz_deflateReset;
416     pub use cloudflare_zlib_sys::inflate as mz_inflate;
417     pub use cloudflare_zlib_sys::inflateEnd as mz_inflateEnd;
418     pub use cloudflare_zlib_sys::z_stream as mz_stream;
419     pub use cloudflare_zlib_sys::*;
420 
421     pub use cloudflare_zlib_sys::Z_BLOCK as MZ_BLOCK;
422     pub use cloudflare_zlib_sys::Z_BUF_ERROR as MZ_BUF_ERROR;
423     pub use cloudflare_zlib_sys::Z_DATA_ERROR as MZ_DATA_ERROR;
424     pub use cloudflare_zlib_sys::Z_DEFAULT_STRATEGY as MZ_DEFAULT_STRATEGY;
425     pub use cloudflare_zlib_sys::Z_DEFLATED as MZ_DEFLATED;
426     pub use cloudflare_zlib_sys::Z_FINISH as MZ_FINISH;
427     pub use cloudflare_zlib_sys::Z_FULL_FLUSH as MZ_FULL_FLUSH;
428     pub use cloudflare_zlib_sys::Z_NEED_DICT as MZ_NEED_DICT;
429     pub use cloudflare_zlib_sys::Z_NO_FLUSH as MZ_NO_FLUSH;
430     pub use cloudflare_zlib_sys::Z_OK as MZ_OK;
431     pub use cloudflare_zlib_sys::Z_PARTIAL_FLUSH as MZ_PARTIAL_FLUSH;
432     pub use cloudflare_zlib_sys::Z_STREAM_END as MZ_STREAM_END;
433     pub use cloudflare_zlib_sys::Z_STREAM_ERROR as MZ_STREAM_ERROR;
434     pub use cloudflare_zlib_sys::Z_SYNC_FLUSH as MZ_SYNC_FLUSH;
435     pub type AllocSize = cloudflare_zlib_sys::uInt;
436 
437     pub const MZ_DEFAULT_WINDOW_BITS: c_int = 15;
438 
439     const ZLIB_VERSION: &'static str = "1.2.8\0";
440 
mz_deflateInit2( stream: *mut mz_stream, level: c_int, method: c_int, window_bits: c_int, mem_level: c_int, strategy: c_int, ) -> c_int441     pub unsafe extern "C" fn mz_deflateInit2(
442         stream: *mut mz_stream,
443         level: c_int,
444         method: c_int,
445         window_bits: c_int,
446         mem_level: c_int,
447         strategy: c_int,
448     ) -> c_int {
449         cloudflare_zlib_sys::deflateInit2_(
450             stream,
451             level,
452             method,
453             window_bits,
454             mem_level,
455             strategy,
456             ZLIB_VERSION.as_ptr() as *const c_char,
457             mem::size_of::<mz_stream>() as c_int,
458         )
459     }
mz_inflateInit2(stream: *mut mz_stream, window_bits: c_int) -> c_int460     pub unsafe extern "C" fn mz_inflateInit2(stream: *mut mz_stream, window_bits: c_int) -> c_int {
461         cloudflare_zlib_sys::inflateInit2_(
462             stream,
463             window_bits,
464             ZLIB_VERSION.as_ptr() as *const c_char,
465             mem::size_of::<mz_stream>() as c_int,
466         )
467     }
468 }
469