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