zlib_rs/
c_api.rs

1#![allow(non_camel_case_types)]
2#![allow(non_snake_case)]
3
4use core::ffi::{c_char, c_int, c_uchar, c_uint, c_ulong, c_void};
5
6use crate::allocate::Allocator;
7
8pub type alloc_func = unsafe extern "C" fn(voidpf, uInt, uInt) -> voidpf;
9pub type free_func = unsafe extern "C" fn(voidpf, voidpf);
10
11pub type Bytef = u8;
12pub type in_func = unsafe extern "C" fn(*mut c_void, *mut *const c_uchar) -> c_uint;
13pub type out_func = unsafe extern "C" fn(*mut c_void, *mut c_uchar, c_uint) -> c_int;
14pub type uInt = c_uint;
15pub type uLong = c_ulong;
16pub type uLongf = c_ulong;
17pub type voidp = *mut c_void;
18pub type voidpc = *const c_void;
19pub type voidpf = *mut c_void;
20
21/// The current stream state
22///
23/// # Custom allocators
24///
25/// The low-level API supports passing in a custom allocator as part of the [`z_stream`]:
26///
27/// ```no_check
28/// struct z_stream {
29///     // ...
30///     zalloc: Option<unsafe extern "C" fn(*mut c_void, c_uint, c_uint) -> *mut c_void>,
31///     zfree: Option<unsafe extern "C" fn(*mut c_void, *mut c_void)>,
32///     opaque: *mut c_void,
33/// }
34/// ```
35///
36/// When these fields are `None` (or `NULL` in C), the initialization functions use a default allocator,
37/// based on feature flags:
38///
39/// - `"rust-allocator"` uses the rust global allocator
40/// - `"c-allocator"` uses an allocator based on `malloc` and `free`
41///
42/// When both are configured, the `"rust-allocator"` is preferred. When no default allocator is configured,
43/// and custom `zalloc` and `zfree` are provided, the initialization functions will return a [`Z_STREAM_ERROR`].
44///
45/// When custom `zalloc` and `zfree` functions are given, they must adhere to the following contract
46/// to be safe:
47///
48/// - a call `zalloc(opaque, n, m)` must return a pointer `p` to `n * m` bytes of memory, or
49///   `NULL` if out of memory
50/// - a call `zfree(opaque, p)` must free that memory
51///
52/// The `strm.opaque` value is passed to as the first argument to all calls to `zalloc`
53/// and `zfree`, but is otherwise ignored by the library.
54#[repr(C)]
55#[derive(Copy, Clone)]
56pub struct z_stream {
57    pub next_in: *const Bytef,
58    pub avail_in: uInt,
59    pub total_in: z_size,
60    pub next_out: *mut Bytef,
61    pub avail_out: uInt,
62    pub total_out: z_size,
63    pub msg: *mut c_char,
64    pub state: *mut internal_state,
65    pub zalloc: Option<alloc_func>,
66    pub zfree: Option<free_func>,
67    pub opaque: voidpf,
68    pub data_type: c_int,
69    pub adler: z_checksum,
70    pub reserved: uLong,
71}
72pub type z_streamp = *mut z_stream;
73
74impl Default for z_stream {
75    fn default() -> Self {
76        let mut stream = Self {
77            next_in: core::ptr::null_mut(),
78            avail_in: 0,
79            total_in: 0,
80            next_out: core::ptr::null_mut(),
81            avail_out: 0,
82            total_out: 0,
83            msg: core::ptr::null_mut(),
84            state: core::ptr::null_mut(),
85            zalloc: None,
86            zfree: None,
87            opaque: core::ptr::null_mut(),
88            data_type: 0,
89            adler: 0,
90            reserved: 0,
91        };
92
93        #[cfg(feature = "rust-allocator")]
94        if stream.zalloc.is_none() || stream.zfree.is_none() {
95            stream.configure_default_rust_allocator()
96        }
97
98        #[cfg(feature = "c-allocator")]
99        if stream.zalloc.is_none() || stream.zfree.is_none() {
100            stream.configure_default_c_allocator()
101        }
102
103        stream
104    }
105}
106
107impl z_stream {
108    fn configure_allocator(&mut self, alloc: Allocator) {
109        self.zalloc = Some(alloc.zalloc);
110        self.zfree = Some(alloc.zfree);
111        self.opaque = alloc.opaque;
112    }
113
114    #[cfg(feature = "rust-allocator")]
115    pub fn configure_default_rust_allocator(&mut self) {
116        self.configure_allocator(crate::allocate::RUST)
117    }
118
119    #[cfg(feature = "c-allocator")]
120    pub fn configure_default_c_allocator(&mut self) {
121        self.configure_allocator(crate::allocate::C)
122    }
123}
124
125// // zlib stores Adler-32 and CRC-32 checksums in unsigned long; zlib-ng uses uint32_t.
126pub(crate) type z_size = c_ulong;
127pub(crate) type z_checksum = c_ulong;
128
129// opaque to the user
130pub enum internal_state {}
131
132pub const Z_NO_FLUSH: c_int = 0;
133pub const Z_PARTIAL_FLUSH: c_int = 1;
134pub const Z_SYNC_FLUSH: c_int = 2;
135pub const Z_FULL_FLUSH: c_int = 3;
136pub const Z_FINISH: c_int = 4;
137pub const Z_BLOCK: c_int = 5;
138pub const Z_TREES: c_int = 6;
139
140pub const Z_OK: c_int = 0;
141pub const Z_STREAM_END: c_int = 1;
142pub const Z_NEED_DICT: c_int = 2;
143pub const Z_ERRNO: c_int = -1;
144pub const Z_STREAM_ERROR: c_int = -2;
145pub const Z_DATA_ERROR: c_int = -3;
146pub const Z_MEM_ERROR: c_int = -4;
147pub const Z_BUF_ERROR: c_int = -5;
148pub const Z_VERSION_ERROR: c_int = -6;
149
150pub const Z_NO_COMPRESSION: c_int = 0;
151pub const Z_BEST_SPEED: c_int = 1;
152pub const Z_BEST_COMPRESSION: c_int = 9;
153pub const Z_DEFAULT_COMPRESSION: c_int = -1;
154
155pub const Z_DEFLATED: c_int = 8;
156
157pub const Z_BINARY: c_int = 0;
158pub const Z_TEXT: c_int = 1;
159pub const Z_ASCII: c_int = Z_TEXT; /* for compatibility with 1.2.2 and earlier */
160pub const Z_UNKNOWN: c_int = 2;
161
162pub const Z_FILTERED: c_int = 1;
163pub const Z_HUFFMAN_ONLY: c_int = 2;
164pub const Z_RLE: c_int = 3;
165pub const Z_FIXED: c_int = 4;
166pub const Z_DEFAULT_STRATEGY: c_int = 0;
167
168pub type gz_headerp = *mut gz_header;
169
170/// gzip header information passed to and from zlib routines.
171/// See RFC 1952 for more details on the meanings of these fields.
172#[derive(Debug)]
173#[repr(C)]
174pub struct gz_header {
175    /// true if compressed data believed to be text
176    pub text: i32,
177    /// modification time
178    pub time: c_ulong,
179    /// extra flags (not used when writing a gzip file)
180    pub xflags: i32,
181    /// operating system
182    pub os: i32,
183    /// pointer to extra field or NULL if none
184    pub extra: *mut u8,
185    /// extra field length (valid if extra != NULL)
186    pub extra_len: u32,
187    /// space at extra (only when reading header)
188    pub extra_max: u32,
189    /// pointer to zero-terminated file name or NULL
190    pub name: *mut u8,
191    /// space at name (only when reading header)
192    pub name_max: u32,
193    /// pointer to zero-terminated comment or NULL
194    pub comment: *mut u8,
195    /// space at comment (only when reading header)
196    pub comm_max: u32,
197    /// true if there was or will be a header crc
198    pub hcrc: i32,
199    /// true when done reading gzip header (not used when writing a gzip file)
200    pub done: i32,
201}
202
203impl Default for gz_header {
204    fn default() -> Self {
205        Self {
206            text: 0,
207            time: 0,
208            xflags: 0,
209            os: 0,
210            extra: core::ptr::null_mut(),
211            extra_len: 0,
212            extra_max: 0,
213            name: core::ptr::null_mut(),
214            name_max: 0,
215            comment: core::ptr::null_mut(),
216            comm_max: 0,
217            hcrc: 0,
218            done: 0,
219        }
220    }
221}
222
223impl gz_header {
224    // based on the spec https://www.ietf.org/rfc/rfc1952.txt
225    //
226    //   0 - FAT filesystem (MS-DOS, OS/2, NT/Win32)
227    //   1 - Amiga
228    //   2 - VMS (or OpenVMS)
229    //   3 - Unix
230    //   4 - VM/CMS
231    //   5 - Atari TOS
232    //   6 - HPFS filesystem (OS/2, NT)
233    //   7 - Macintosh
234    //   8 - Z-System
235    //   9 - CP/M
236    //  10 - TOPS-20
237    //  11 - NTFS filesystem (NT)
238    //  12 - QDOS
239    //  13 - Acorn RISCOS
240    // 255 - unknown
241    #[allow(clippy::if_same_then_else)]
242    pub const OS_CODE: u8 = {
243        if cfg!(windows) {
244            10
245        } else if cfg!(target_os = "macos") {
246            19
247        } else if cfg!(unix) {
248            3
249        } else {
250            3 // assume unix
251        }
252    };
253
254    pub(crate) fn flags(&self) -> u8 {
255        (if self.text != 0 { 1 } else { 0 })
256            + (if self.hcrc != 0 { 2 } else { 0 })
257            + (if self.extra.is_null() { 0 } else { 4 })
258            + (if self.name.is_null() { 0 } else { 8 })
259            + (if self.comment.is_null() { 0 } else { 16 })
260    }
261}