Skip to main content

zlib_rs/
lib.rs

1#![doc = core::include_str!("../README.md")]
2#![cfg_attr(not(any(test, feature = "rust-allocator")), no_std)]
3// For testing the loongarch64 crc32 implementation.
4#![cfg_attr(all(miri, target_arch = "loongarch64"), feature(stdarch_loongarch))]
5
6#[cfg(any(feature = "rust-allocator", feature = "c-allocator"))]
7extern crate alloc;
8
9pub mod adler32;
10pub mod crc32;
11
12cfg_select! {
13    feature = "__internal-api" => {
14        pub mod allocate;
15        pub mod c_api;
16        pub mod deflate;
17        pub mod inflate;
18
19        pub const MIN_WBITS: i32 = 8; // 256b LZ77 window
20        pub const MAX_WBITS: i32 = 15; // 32kb LZ77 window
21    }
22    _ => {
23        pub(crate) mod allocate;
24        pub(crate) mod c_api;
25        pub(crate) mod deflate;
26        pub(crate) mod inflate;
27
28        pub(crate) const MIN_WBITS: i32 = 8; // 256b LZ77 window
29        pub(crate) const MAX_WBITS: i32 = 15; // 32kb LZ77 window
30    }
31}
32
33mod cpu_features;
34mod stable;
35mod weak_slice;
36
37pub use stable::{Deflate, DeflateError, Inflate, InflateError, Status};
38
39pub use deflate::{DeflateConfig, Method, Strategy};
40pub use inflate::InflateConfig;
41
42pub use deflate::{compress_bound, compress_slice};
43pub use inflate::decompress_slice;
44
45macro_rules! trace {
46    ($($arg:tt)*) => {
47        #[cfg(feature = "ZLIB_DEBUG")]
48        {
49            eprint!($($arg)*)
50        }
51    };
52}
53pub(crate) use trace;
54
55macro_rules! cfg_select {
56    ({ $($tt:tt)* }) => {{
57        $crate::cfg_select! { $($tt)* }
58    }};
59    (_ => { $($output:tt)* }) => {
60        $($output)*
61    };
62    (
63        $cfg:meta => $output:tt
64        $($( $rest:tt )+)?
65    ) => {
66        #[cfg($cfg)]
67        $crate::cfg_select! { _ => $output }
68        $(
69            #[cfg(not($cfg))]
70            $crate::cfg_select! { $($rest)+ }
71        )?
72    }
73}
74use cfg_select;
75
76/// Maximum size of the dynamic table.  The maximum number of code structures is
77/// 1924, which is the sum of 1332 for literal/length codes and 592 for distance
78/// codes.  These values were found by exhaustive searches using the program
79/// examples/enough.c found in the zlib distributions.  The arguments to that
80/// program are the number of symbols, the initial root table size, and the
81/// maximum bit length of a code.  "enough 286 10 15" for literal/length codes
82/// returns 1332, and "enough 30 9 15" for distance codes returns 592.
83/// The initial root table size (10 or 9) is found in the fifth argument of the
84/// inflate_table() calls in inflate.c and infback.c.  If the root table size is
85/// changed, then these maximum sizes would be need to be recalculated and
86/// updated.
87#[allow(unused)]
88pub(crate) const ENOUGH: usize = ENOUGH_LENS + ENOUGH_DISTS;
89pub(crate) const ENOUGH_LENS: usize = 1332;
90pub(crate) const ENOUGH_DISTS: usize = 592;
91
92/// initial adler-32 hash value
93pub(crate) const ADLER32_INITIAL_VALUE: usize = 1;
94/// initial crc-32 hash value
95pub(crate) const CRC32_INITIAL_VALUE: u32 = 0;
96
97pub(crate) const DEF_WBITS: i32 = MAX_WBITS;
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
100#[cfg_attr(feature = "__internal-fuzz", derive(arbitrary::Arbitrary))]
101pub enum DeflateFlush {
102    #[default]
103    /// if flush is set to `NoFlush`, that allows deflate to decide how much data
104    /// to accumulate before producing output, in order to maximize compression.
105    NoFlush = 0,
106
107    /// If flush is set to `PartialFlush`, all pending output is flushed to the
108    /// output buffer, but the output is not aligned to a byte boundary.  All of the
109    /// input data so far will be available to the decompressor, as for Z_SYNC_FLUSH.
110    /// This completes the current deflate block and follows it with an empty fixed
111    /// codes block that is 10 bits long.  This assures that enough bytes are output
112    /// in order for the decompressor to finish the block before the empty fixed
113    /// codes block.
114    PartialFlush = 1,
115
116    /// If the parameter flush is set to `SyncFlush`, all pending output is
117    /// flushed to the output buffer and the output is aligned on a byte boundary, so
118    /// that the decompressor can get all input data available so far.  (In
119    /// particular avail_in is zero after the call if enough output space has been
120    /// provided before the call.) Flushing may degrade compression for some
121    /// compression algorithms and so it should be used only when necessary.  This
122    /// completes the current deflate block and follows it with an empty stored block
123    /// that is three bits plus filler bits to the next byte, followed by four bytes
124    /// (00 00 ff ff).
125    SyncFlush = 2,
126
127    /// If flush is set to `FullFlush`, all output is flushed as with
128    /// Z_SYNC_FLUSH, and the compression state is reset so that decompression can
129    /// restart from this point if previous compressed data has been damaged or if
130    /// random access is desired.  Using `FullFlush` too often can seriously degrade
131    /// compression.
132    FullFlush = 3,
133
134    /// If the parameter flush is set to `Finish`, pending input is processed,
135    /// pending output is flushed and deflate returns with `StreamEnd` if there was
136    /// enough output space.  If deflate returns with `Ok` or `BufError`, this
137    /// function must be called again with `Finish` and more output space (updated
138    /// avail_out) but no more input data, until it returns with `StreamEnd` or an
139    /// error.  After deflate has returned `StreamEnd`, the only possible operations
140    /// on the stream are deflateReset or deflateEnd.
141    ///
142    /// `Finish` can be used in the first deflate call after deflateInit if all the
143    /// compression is to be done in a single step.  In order to complete in one
144    /// call, avail_out must be at least the value returned by deflateBound (see
145    /// below).  Then deflate is guaranteed to return `StreamEnd`.  If not enough
146    /// output space is provided, deflate will not return `StreamEnd`, and it must
147    /// be called again as described above.
148    Finish = 4,
149
150    /// If flush is set to `Block`, a deflate block is completed and emitted, as
151    /// for `SyncFlush`, but the output is not aligned on a byte boundary, and up to
152    /// seven bits of the current block are held to be written as the next byte after
153    /// the next deflate block is completed.  In this case, the decompressor may not
154    /// be provided enough bits at this point in order to complete decompression of
155    /// the data provided so far to the compressor.  It may need to wait for the next
156    /// block to be emitted.  This is for advanced applications that need to control
157    /// the emission of deflate blocks.
158    Block = 5,
159}
160
161impl TryFrom<i32> for DeflateFlush {
162    type Error = ();
163
164    fn try_from(value: i32) -> Result<Self, Self::Error> {
165        match value {
166            0 => Ok(Self::NoFlush),
167            1 => Ok(Self::PartialFlush),
168            2 => Ok(Self::SyncFlush),
169            3 => Ok(Self::FullFlush),
170            4 => Ok(Self::Finish),
171            5 => Ok(Self::Block),
172            _ => Err(()),
173        }
174    }
175}
176
177#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
178pub enum InflateFlush {
179    #[default]
180    NoFlush = 0,
181    SyncFlush = 2,
182    Finish = 4,
183    Block = 5,
184    Trees = 6,
185}
186
187impl TryFrom<i32> for InflateFlush {
188    type Error = ();
189
190    fn try_from(value: i32) -> Result<Self, Self::Error> {
191        match value {
192            0 => Ok(Self::NoFlush),
193            2 => Ok(Self::SyncFlush),
194            4 => Ok(Self::Finish),
195            5 => Ok(Self::Block),
196            6 => Ok(Self::Trees),
197            _ => Err(()),
198        }
199    }
200}
201
202#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
203pub(crate) struct Code {
204    /// operation, extra bits, table bits
205    pub op: u8,
206    /// bits in this part of the code
207    pub bits: u8,
208    /// offset in table or code value
209    pub val: u16,
210}
211
212#[derive(Debug, Copy, Clone, PartialEq, Eq)]
213#[repr(i32)]
214pub enum ReturnCode {
215    Ok = 0,
216    StreamEnd = 1,
217    NeedDict = 2,
218    ErrNo = -1,
219    StreamError = -2,
220    DataError = -3,
221    MemError = -4,
222    BufError = -5,
223    VersionError = -6,
224}
225
226impl From<i32> for ReturnCode {
227    fn from(value: i32) -> Self {
228        match Self::try_from_c_int(value) {
229            Some(value) => value,
230            None => panic!("invalid return code {value}"),
231        }
232    }
233}
234
235impl ReturnCode {
236    fn error_message_str(self) -> &'static str {
237        self.error_message_str_with_null().trim_end_matches('\0')
238    }
239
240    const fn error_message_str_with_null(self) -> &'static str {
241        match self {
242            ReturnCode::Ok => "\0",
243            ReturnCode::StreamEnd => "stream end\0",
244            ReturnCode::NeedDict => "need dictionary\0",
245            ReturnCode::ErrNo => "file error\0",
246            ReturnCode::StreamError => "stream error\0",
247            ReturnCode::DataError => "data error\0",
248            ReturnCode::MemError => "insufficient memory\0",
249            ReturnCode::BufError => "buffer error\0",
250            ReturnCode::VersionError => "incompatible version\0",
251        }
252    }
253
254    pub const fn error_message(self) -> *const core::ffi::c_char {
255        let msg = self.error_message_str_with_null();
256        msg.as_ptr().cast::<core::ffi::c_char>()
257    }
258
259    pub const fn try_from_c_int(err: core::ffi::c_int) -> Option<Self> {
260        match err {
261            0 => Some(Self::Ok),
262            1 => Some(Self::StreamEnd),
263            2 => Some(Self::NeedDict),
264            -1 => Some(Self::ErrNo),
265            -2 => Some(Self::StreamError),
266            -3 => Some(Self::DataError),
267            -4 => Some(Self::MemError),
268            -5 => Some(Self::BufError),
269            -6 => Some(Self::VersionError),
270            _ => None,
271        }
272    }
273}