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 assert!(
168 window_bits > 8 && window_bits < 16,
169 "window_bits must be within 9 ..= 15"
170 );
171 unsafe {
172 let mut state = StreamWrapper::default();
173 let ret = mz_inflateInit2(
174 &mut *state,
175 if zlib_header {
176 window_bits as c_int
177 } else {
178 -(window_bits as c_int)
179 },
180 );
181 assert_eq!(ret, 0);
182 Inflate {
183 inner: Stream {
184 stream_wrapper: state,
185 total_in: 0,
186 total_out: 0,
187 _marker: marker::PhantomData,
188 },
189 }
190 }
191 }
192
decompress( &mut self, input: &[u8], output: &mut [u8], flush: FlushDecompress, ) -> Result<Status, DecompressError>193 fn decompress(
194 &mut self,
195 input: &[u8],
196 output: &mut [u8],
197 flush: FlushDecompress,
198 ) -> Result<Status, DecompressError> {
199 let raw = &mut *self.inner.stream_wrapper;
200 raw.next_in = input.as_ptr() as *mut u8;
201 raw.avail_in = cmp::min(input.len(), c_uint::max_value() as usize) as c_uint;
202 raw.next_out = output.as_mut_ptr();
203 raw.avail_out = cmp::min(output.len(), c_uint::max_value() as usize) as c_uint;
204
205 let rc = unsafe { mz_inflate(raw, flush as c_int) };
206
207 // Unfortunately the total counters provided by zlib might be only
208 // 32 bits wide and overflow while processing large amounts of data.
209 self.inner.total_in += (raw.next_in as usize - input.as_ptr() as usize) as u64;
210 self.inner.total_out += (raw.next_out as usize - output.as_ptr() as usize) as u64;
211
212 match rc {
213 MZ_DATA_ERROR | MZ_STREAM_ERROR => mem::decompress_failed(),
214 MZ_OK => Ok(Status::Ok),
215 MZ_BUF_ERROR => Ok(Status::BufError),
216 MZ_STREAM_END => Ok(Status::StreamEnd),
217 MZ_NEED_DICT => mem::decompress_need_dict(raw.adler as u32),
218 c => panic!("unknown return code: {}", c),
219 }
220 }
221
222 #[cfg(feature = "any_zlib")]
reset(&mut self, zlib_header: bool)223 fn reset(&mut self, zlib_header: bool) {
224 let bits = if zlib_header {
225 MZ_DEFAULT_WINDOW_BITS
226 } else {
227 -MZ_DEFAULT_WINDOW_BITS
228 };
229 unsafe {
230 inflateReset2(&mut *self.inner.stream_wrapper, bits);
231 }
232 self.inner.total_out = 0;
233 self.inner.total_in = 0;
234 }
235
236 #[cfg(not(feature = "any_zlib"))]
reset(&mut self, zlib_header: bool)237 fn reset(&mut self, zlib_header: bool) {
238 *self = Self::make(zlib_header, MZ_DEFAULT_WINDOW_BITS as u8);
239 }
240 }
241
242 impl Backend for Inflate {
243 #[inline]
total_in(&self) -> u64244 fn total_in(&self) -> u64 {
245 self.inner.total_in
246 }
247
248 #[inline]
total_out(&self) -> u64249 fn total_out(&self) -> u64 {
250 self.inner.total_out
251 }
252 }
253
254 #[derive(Debug)]
255 pub struct Deflate {
256 pub inner: Stream<DirCompress>,
257 }
258
259 impl DeflateBackend for Deflate {
make(level: Compression, zlib_header: bool, window_bits: u8) -> Self260 fn make(level: Compression, zlib_header: bool, window_bits: u8) -> Self {
261 assert!(
262 window_bits > 8 && window_bits < 16,
263 "window_bits must be within 9 ..= 15"
264 );
265 unsafe {
266 let mut state = StreamWrapper::default();
267 let ret = mz_deflateInit2(
268 &mut *state,
269 level.0 as c_int,
270 MZ_DEFLATED,
271 if zlib_header {
272 window_bits as c_int
273 } else {
274 -(window_bits as c_int)
275 },
276 9,
277 MZ_DEFAULT_STRATEGY,
278 );
279 assert_eq!(ret, 0);
280 Deflate {
281 inner: Stream {
282 stream_wrapper: state,
283 total_in: 0,
284 total_out: 0,
285 _marker: marker::PhantomData,
286 },
287 }
288 }
289 }
compress( &mut self, input: &[u8], output: &mut [u8], flush: FlushCompress, ) -> Result<Status, CompressError>290 fn compress(
291 &mut self,
292 input: &[u8],
293 output: &mut [u8],
294 flush: FlushCompress,
295 ) -> Result<Status, CompressError> {
296 let raw = &mut *self.inner.stream_wrapper;
297 raw.next_in = input.as_ptr() as *mut _;
298 raw.avail_in = cmp::min(input.len(), c_uint::max_value() as usize) as c_uint;
299 raw.next_out = output.as_mut_ptr();
300 raw.avail_out = cmp::min(output.len(), c_uint::max_value() as usize) as c_uint;
301
302 let rc = unsafe { mz_deflate(raw, flush as c_int) };
303
304 // Unfortunately the total counters provided by zlib might be only
305 // 32 bits wide and overflow while processing large amounts of data.
306 self.inner.total_in += (raw.next_in as usize - input.as_ptr() as usize) as u64;
307 self.inner.total_out += (raw.next_out as usize - output.as_ptr() as usize) as u64;
308
309 match rc {
310 MZ_OK => Ok(Status::Ok),
311 MZ_BUF_ERROR => Ok(Status::BufError),
312 MZ_STREAM_END => Ok(Status::StreamEnd),
313 MZ_STREAM_ERROR => Err(CompressError(())),
314 c => panic!("unknown return code: {}", c),
315 }
316 }
317
reset(&mut self)318 fn reset(&mut self) {
319 self.inner.total_in = 0;
320 self.inner.total_out = 0;
321 let rc = unsafe { mz_deflateReset(&mut *self.inner.stream_wrapper) };
322 assert_eq!(rc, MZ_OK);
323 }
324 }
325
326 impl Backend for Deflate {
327 #[inline]
total_in(&self) -> u64328 fn total_in(&self) -> u64 {
329 self.inner.total_in
330 }
331
332 #[inline]
total_out(&self) -> u64333 fn total_out(&self) -> u64 {
334 self.inner.total_out
335 }
336 }
337
338 pub use self::c_backend::*;
339
340 /// Miniz specific
341 #[cfg(not(feature = "any_zlib"))]
342 mod c_backend {
343 pub use miniz_sys::*;
344 pub type AllocSize = libc::size_t;
345 }
346
347 /// Zlib specific
348 #[cfg(all(feature = "zlib", not(feature = "cloudflare_zlib")))]
349 #[allow(bad_style)]
350 mod c_backend {
351 use libc::{c_char, c_int};
352 use std::mem;
353
354 pub use libz_sys::deflate as mz_deflate;
355 pub use libz_sys::deflateEnd as mz_deflateEnd;
356 pub use libz_sys::deflateReset as mz_deflateReset;
357 pub use libz_sys::inflate as mz_inflate;
358 pub use libz_sys::inflateEnd as mz_inflateEnd;
359 pub use libz_sys::z_stream as mz_stream;
360 pub use libz_sys::*;
361
362 pub use libz_sys::Z_BLOCK as MZ_BLOCK;
363 pub use libz_sys::Z_BUF_ERROR as MZ_BUF_ERROR;
364 pub use libz_sys::Z_DATA_ERROR as MZ_DATA_ERROR;
365 pub use libz_sys::Z_DEFAULT_STRATEGY as MZ_DEFAULT_STRATEGY;
366 pub use libz_sys::Z_DEFLATED as MZ_DEFLATED;
367 pub use libz_sys::Z_FINISH as MZ_FINISH;
368 pub use libz_sys::Z_FULL_FLUSH as MZ_FULL_FLUSH;
369 pub use libz_sys::Z_NEED_DICT as MZ_NEED_DICT;
370 pub use libz_sys::Z_NO_FLUSH as MZ_NO_FLUSH;
371 pub use libz_sys::Z_OK as MZ_OK;
372 pub use libz_sys::Z_PARTIAL_FLUSH as MZ_PARTIAL_FLUSH;
373 pub use libz_sys::Z_STREAM_END as MZ_STREAM_END;
374 pub use libz_sys::Z_STREAM_ERROR as MZ_STREAM_ERROR;
375 pub use libz_sys::Z_SYNC_FLUSH as MZ_SYNC_FLUSH;
376 pub type AllocSize = libz_sys::uInt;
377
378 pub const MZ_DEFAULT_WINDOW_BITS: c_int = 15;
379
380 const ZLIB_VERSION: &'static str = "1.2.8\0";
381
mz_deflateInit2( stream: *mut mz_stream, level: c_int, method: c_int, window_bits: c_int, mem_level: c_int, strategy: c_int, ) -> c_int382 pub unsafe extern "C" fn mz_deflateInit2(
383 stream: *mut mz_stream,
384 level: c_int,
385 method: c_int,
386 window_bits: c_int,
387 mem_level: c_int,
388 strategy: c_int,
389 ) -> c_int {
390 libz_sys::deflateInit2_(
391 stream,
392 level,
393 method,
394 window_bits,
395 mem_level,
396 strategy,
397 ZLIB_VERSION.as_ptr() as *const c_char,
398 mem::size_of::<mz_stream>() as c_int,
399 )
400 }
mz_inflateInit2(stream: *mut mz_stream, window_bits: c_int) -> c_int401 pub unsafe extern "C" fn mz_inflateInit2(stream: *mut mz_stream, window_bits: c_int) -> c_int {
402 libz_sys::inflateInit2_(
403 stream,
404 window_bits,
405 ZLIB_VERSION.as_ptr() as *const c_char,
406 mem::size_of::<mz_stream>() as c_int,
407 )
408 }
409 }
410
411 /// Cloudflare optimized Zlib specific
412 #[cfg(feature = "cloudflare_zlib")]
413 #[allow(bad_style)]
414 mod c_backend {
415 use libc::{c_char, c_int};
416 use std::mem;
417
418 pub use cloudflare_zlib_sys::deflate as mz_deflate;
419 pub use cloudflare_zlib_sys::deflateEnd as mz_deflateEnd;
420 pub use cloudflare_zlib_sys::deflateReset as mz_deflateReset;
421 pub use cloudflare_zlib_sys::inflate as mz_inflate;
422 pub use cloudflare_zlib_sys::inflateEnd as mz_inflateEnd;
423 pub use cloudflare_zlib_sys::z_stream as mz_stream;
424 pub use cloudflare_zlib_sys::*;
425
426 pub use cloudflare_zlib_sys::Z_BLOCK as MZ_BLOCK;
427 pub use cloudflare_zlib_sys::Z_BUF_ERROR as MZ_BUF_ERROR;
428 pub use cloudflare_zlib_sys::Z_DATA_ERROR as MZ_DATA_ERROR;
429 pub use cloudflare_zlib_sys::Z_DEFAULT_STRATEGY as MZ_DEFAULT_STRATEGY;
430 pub use cloudflare_zlib_sys::Z_DEFLATED as MZ_DEFLATED;
431 pub use cloudflare_zlib_sys::Z_FINISH as MZ_FINISH;
432 pub use cloudflare_zlib_sys::Z_FULL_FLUSH as MZ_FULL_FLUSH;
433 pub use cloudflare_zlib_sys::Z_NEED_DICT as MZ_NEED_DICT;
434 pub use cloudflare_zlib_sys::Z_NO_FLUSH as MZ_NO_FLUSH;
435 pub use cloudflare_zlib_sys::Z_OK as MZ_OK;
436 pub use cloudflare_zlib_sys::Z_PARTIAL_FLUSH as MZ_PARTIAL_FLUSH;
437 pub use cloudflare_zlib_sys::Z_STREAM_END as MZ_STREAM_END;
438 pub use cloudflare_zlib_sys::Z_STREAM_ERROR as MZ_STREAM_ERROR;
439 pub use cloudflare_zlib_sys::Z_SYNC_FLUSH as MZ_SYNC_FLUSH;
440 pub type AllocSize = cloudflare_zlib_sys::uInt;
441
442 pub const MZ_DEFAULT_WINDOW_BITS: c_int = 15;
443
444 const ZLIB_VERSION: &'static str = "1.2.8\0";
445
mz_deflateInit2( stream: *mut mz_stream, level: c_int, method: c_int, window_bits: c_int, mem_level: c_int, strategy: c_int, ) -> c_int446 pub unsafe extern "C" fn mz_deflateInit2(
447 stream: *mut mz_stream,
448 level: c_int,
449 method: c_int,
450 window_bits: c_int,
451 mem_level: c_int,
452 strategy: c_int,
453 ) -> c_int {
454 cloudflare_zlib_sys::deflateInit2_(
455 stream,
456 level,
457 method,
458 window_bits,
459 mem_level,
460 strategy,
461 ZLIB_VERSION.as_ptr() as *const c_char,
462 mem::size_of::<mz_stream>() as c_int,
463 )
464 }
mz_inflateInit2(stream: *mut mz_stream, window_bits: c_int) -> c_int465 pub unsafe extern "C" fn mz_inflateInit2(stream: *mut mz_stream, window_bits: c_int) -> c_int {
466 cloudflare_zlib_sys::inflateInit2_(
467 stream,
468 window_bits,
469 ZLIB_VERSION.as_ptr() as *const c_char,
470 mem::size_of::<mz_stream>() as c_int,
471 )
472 }
473 }
474