zlib_rs/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
#![doc = core::include_str!("../README.md")]
#![cfg_attr(not(any(test, feature = "rust-allocator")), no_std)]

#[cfg(any(feature = "rust-allocator", feature = "c-allocator"))]
extern crate alloc;

mod adler32;
pub mod allocate;
pub mod c_api;
mod cpu_features;
pub mod crc32;
pub mod deflate;
pub mod inflate;
pub mod read_buf;
mod weak_slice;

pub use adler32::{adler32, adler32_combine};
pub use crc32::{crc32, crc32_combine};

#[macro_export]
macro_rules! trace {
    ($($arg:tt)*) => {
        #[cfg(feature = "ZLIB_DEBUG")]
        {
            eprint!($($arg)*)
        }
    };
}

/// Maximum size of the dynamic table.  The maximum number of code structures is
/// 1924, which is the sum of 1332 for literal/length codes and 592 for distance
/// codes.  These values were found by exhaustive searches using the program
/// examples/enough.c found in the zlib distributions.  The arguments to that
/// program are the number of symbols, the initial root table size, and the
/// maximum bit length of a code.  "enough 286 10 15" for literal/length codes
/// returns 1332, and "enough 30 9 15" for distance codes returns 592.
/// The initial root table size (10 or 9) is found in the fifth argument of the
/// inflate_table() calls in inflate.c and infback.c.  If the root table size is
/// changed, then these maximum sizes would be need to be recalculated and
/// updated.
#[allow(unused)]
pub(crate) const ENOUGH: usize = ENOUGH_LENS + ENOUGH_DISTS;
pub(crate) const ENOUGH_LENS: usize = 1332;
pub(crate) const ENOUGH_DISTS: usize = 592;

/// initial adler-32 hash value
pub(crate) const ADLER32_INITIAL_VALUE: usize = 1;
/// initial crc-32 hash value
pub(crate) const CRC32_INITIAL_VALUE: u32 = 0;

pub const MIN_WBITS: i32 = 8; // 256b LZ77 window
pub const MAX_WBITS: i32 = 15; // 32kb LZ77 window
pub(crate) const DEF_WBITS: i32 = MAX_WBITS;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum DeflateFlush {
    #[default]
    /// if flush is set to `NoFlush`, that allows deflate to decide how much data
    /// to accumulate before producing output, in order to maximize compression.
    NoFlush = 0,

    /// If flush is set to `PartialFlush`, all pending output is flushed to the
    /// output buffer, but the output is not aligned to a byte boundary.  All of the
    /// input data so far will be available to the decompressor, as for Z_SYNC_FLUSH.
    /// This completes the current deflate block and follows it with an empty fixed
    /// codes block that is 10 bits long.  This assures that enough bytes are output
    /// in order for the decompressor to finish the block before the empty fixed
    /// codes block.
    PartialFlush = 1,

    /// If the parameter flush is set to `SyncFlush`, all pending output is
    /// flushed to the output buffer and the output is aligned on a byte boundary, so
    /// that the decompressor can get all input data available so far.  (In
    /// particular avail_in is zero after the call if enough output space has been
    /// provided before the call.) Flushing may degrade compression for some
    /// compression algorithms and so it should be used only when necessary.  This
    /// completes the current deflate block and follows it with an empty stored block
    /// that is three bits plus filler bits to the next byte, followed by four bytes
    /// (00 00 ff ff).
    SyncFlush = 2,

    /// If flush is set to `FullFlush`, all output is flushed as with
    /// Z_SYNC_FLUSH, and the compression state is reset so that decompression can
    /// restart from this point if previous compressed data has been damaged or if
    /// random access is desired.  Using `FullFlush` too often can seriously degrade
    /// compression.
    FullFlush = 3,

    /// If the parameter flush is set to `Finish`, pending input is processed,
    /// pending output is flushed and deflate returns with `StreamEnd` if there was
    /// enough output space.  If deflate returns with `Ok` or `BufError`, this
    /// function must be called again with `Finish` and more output space (updated
    /// avail_out) but no more input data, until it returns with `StreamEnd` or an
    /// error.  After deflate has returned `StreamEnd`, the only possible operations
    /// on the stream are deflateReset or deflateEnd.
    ///
    /// `Finish` can be used in the first deflate call after deflateInit if all the
    /// compression is to be done in a single step.  In order to complete in one
    /// call, avail_out must be at least the value returned by deflateBound (see
    /// below).  Then deflate is guaranteed to return `StreamEnd`.  If not enough
    /// output space is provided, deflate will not return `StreamEnd`, and it must
    /// be called again as described above.
    Finish = 4,

    /// If flush is set to `Block`, a deflate block is completed and emitted, as
    /// for `SyncFlush`, but the output is not aligned on a byte boundary, and up to
    /// seven bits of the current block are held to be written as the next byte after
    /// the next deflate block is completed.  In this case, the decompressor may not
    /// be provided enough bits at this point in order to complete decompression of
    /// the data provided so far to the compressor.  It may need to wait for the next
    /// block to be emitted.  This is for advanced applications that need to control
    /// the emission of deflate blocks.
    Block = 5,
}

impl TryFrom<i32> for DeflateFlush {
    type Error = ();

    fn try_from(value: i32) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(Self::NoFlush),
            1 => Ok(Self::PartialFlush),
            2 => Ok(Self::SyncFlush),
            3 => Ok(Self::FullFlush),
            4 => Ok(Self::Finish),
            5 => Ok(Self::Block),
            _ => Err(()),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum InflateFlush {
    #[default]
    NoFlush = 0,
    SyncFlush = 2,
    Finish = 4,
    Block = 5,
    Trees = 6,
}

impl TryFrom<i32> for InflateFlush {
    type Error = ();

    fn try_from(value: i32) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(Self::NoFlush),
            2 => Ok(Self::SyncFlush),
            4 => Ok(Self::Finish),
            5 => Ok(Self::Block),
            6 => Ok(Self::Trees),
            _ => Err(()),
        }
    }
}

#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub(crate) struct Code {
    /// operation, extra bits, table bits
    pub op: u8,
    /// bits in this part of the code
    pub bits: u8,
    /// offset in table or code value
    pub val: u16,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(i32)]
pub enum ReturnCode {
    Ok = 0,
    StreamEnd = 1,
    NeedDict = 2,
    ErrNo = -1,
    StreamError = -2,
    DataError = -3,
    MemError = -4,
    BufError = -5,
    VersionError = -6,
}

impl From<i32> for ReturnCode {
    fn from(value: i32) -> Self {
        match Self::try_from_c_int(value) {
            Some(value) => value,
            None => panic!("invalid return code {value}"),
        }
    }
}

impl ReturnCode {
    const fn error_message_str(self) -> &'static str {
        match self {
            ReturnCode::Ok => "\0",
            ReturnCode::StreamEnd => "stream end\0",
            ReturnCode::NeedDict => "need dictionary\0",
            ReturnCode::ErrNo => "file error\0",
            ReturnCode::StreamError => "stream error\0",
            ReturnCode::DataError => "data error\0",
            ReturnCode::MemError => "insufficient memory\0",
            ReturnCode::BufError => "buffer error\0",
            ReturnCode::VersionError => "incompatible version\0",
        }
    }

    pub const fn error_message(self) -> *const core::ffi::c_char {
        let msg = self.error_message_str();
        msg.as_ptr().cast::<core::ffi::c_char>()
    }

    pub const fn try_from_c_int(err: core::ffi::c_int) -> Option<Self> {
        match err {
            0 => Some(Self::Ok),
            1 => Some(Self::StreamEnd),
            2 => Some(Self::NeedDict),
            -1 => Some(Self::ErrNo),
            -2 => Some(Self::StreamError),
            -3 => Some(Self::DataError),
            -4 => Some(Self::MemError),
            -5 => Some(Self::BufError),
            -6 => Some(Self::VersionError),
            _ => None,
        }
    }
}