Skip to content

Commit b77744f

Browse files
authored
Merge pull request #2298 from GitoxideLabs/copilot/replace-libz-rs-sys-with-zlib-rs
Replace libz-rs-sys FFI wrapper with direct zlib-rs usage
2 parents d207075 + 2384d0c commit b77744f

File tree

4 files changed

+75
-98
lines changed

4 files changed

+75
-98
lines changed

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gix-features/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ crc32 = ["dep:crc32fast"]
5757

5858
## Enable the usage of zlib-related utilities to compress or decompress data.
5959
## This enables and uses the high-performance `zlib-rs` backend.
60-
zlib = ["dep:libz-rs-sys", "dep:thiserror"]
60+
zlib = ["dep:zlib-rs", "dep:thiserror"]
6161

6262
#! ### Other
6363

@@ -108,7 +108,7 @@ bytesize = { version = "2.3.1", optional = true }
108108
bytes = { version = "1.0.0", optional = true }
109109

110110
# zlib module
111-
libz-rs-sys = { version = "0.5.2", optional = true }
111+
zlib-rs = { version = "0.5.4", optional = true }
112112
thiserror = { version = "2.0.17", optional = true }
113113

114114
# Note: once_cell is kept for OnceCell type because std::sync::OnceLock::get_or_try_init() is not yet stable.

gix-features/src/zlib/mod.rs

Lines changed: 34 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
use std::ffi::c_int;
1+
use zlib_rs::InflateError;
22

33
/// A type to hold all state needed for decompressing a ZLIB encoded stream.
4-
pub struct Decompress(libz_rs_sys::z_stream);
5-
6-
unsafe impl Sync for Decompress {}
7-
unsafe impl Send for Decompress {}
4+
pub struct Decompress(zlib_rs::Inflate);
85

96
impl Default for Decompress {
107
fn default() -> Self {
@@ -15,32 +12,23 @@ impl Default for Decompress {
1512
impl Decompress {
1613
/// The amount of bytes consumed from the input so far.
1714
pub fn total_in(&self) -> u64 {
18-
self.0.total_in as _
15+
self.0.total_in()
1916
}
2017

2118
/// The amount of decompressed bytes that have been written to the output thus far.
2219
pub fn total_out(&self) -> u64 {
23-
self.0.total_out as _
20+
self.0.total_out()
2421
}
2522

2623
/// Create a new instance. Note that it allocates in various ways and thus should be re-used.
2724
pub fn new() -> Self {
28-
let mut this = libz_rs_sys::z_stream::default();
29-
30-
unsafe {
31-
libz_rs_sys::inflateInit_(
32-
&mut this,
33-
libz_rs_sys::zlibVersion(),
34-
core::mem::size_of::<libz_rs_sys::z_stream>() as core::ffi::c_int,
35-
);
36-
}
37-
38-
Self(this)
25+
let inner = zlib_rs::Inflate::new(true, zlib_rs::MAX_WBITS as u8);
26+
Self(inner)
3927
}
4028

4129
/// Reset the state to allow handling a new stream.
4230
pub fn reset(&mut self) {
43-
unsafe { libz_rs_sys::inflateReset(&mut self.0) };
31+
self.0.reset(true);
4432
}
4533

4634
/// Decompress `input` and write all decompressed bytes into `output`, with `flush` defining some details about this.
@@ -50,31 +38,21 @@ impl Decompress {
5038
output: &mut [u8],
5139
flush: FlushDecompress,
5240
) -> Result<Status, DecompressError> {
53-
self.0.avail_in = input.len() as _;
54-
self.0.avail_out = output.len() as _;
55-
56-
self.0.next_in = input.as_ptr();
57-
self.0.next_out = output.as_mut_ptr();
58-
59-
match unsafe { libz_rs_sys::inflate(&mut self.0, flush as _) } {
60-
libz_rs_sys::Z_OK => Ok(Status::Ok),
61-
libz_rs_sys::Z_BUF_ERROR => Ok(Status::BufError),
62-
libz_rs_sys::Z_STREAM_END => Ok(Status::StreamEnd),
63-
64-
libz_rs_sys::Z_STREAM_ERROR => Err(DecompressError::StreamError),
65-
libz_rs_sys::Z_DATA_ERROR => Err(DecompressError::DataError),
66-
libz_rs_sys::Z_MEM_ERROR => Err(DecompressError::InsufficientMemory),
67-
err => Err(DecompressError::Unknown { err }),
41+
let inflate_flush = match flush {
42+
FlushDecompress::None => zlib_rs::InflateFlush::NoFlush,
43+
FlushDecompress::Sync => zlib_rs::InflateFlush::SyncFlush,
44+
FlushDecompress::Finish => zlib_rs::InflateFlush::Finish,
45+
};
46+
47+
let status = self.0.decompress(input, output, inflate_flush)?;
48+
match status {
49+
zlib_rs::Status::Ok => Ok(Status::Ok),
50+
zlib_rs::Status::BufError => Ok(Status::BufError),
51+
zlib_rs::Status::StreamEnd => Ok(Status::StreamEnd),
6852
}
6953
}
7054
}
7155

72-
impl Drop for Decompress {
73-
fn drop(&mut self) {
74-
unsafe { libz_rs_sys::inflateEnd(&mut self.0) };
75-
}
76-
}
77-
7856
/// The error produced by [`Decompress::decompress()`].
7957
#[derive(Debug, thiserror::Error)]
8058
#[allow(missing_docs)]
@@ -85,8 +63,19 @@ pub enum DecompressError {
8563
InsufficientMemory,
8664
#[error("Invalid input data")]
8765
DataError,
88-
#[error("An unknown error occurred: {err}")]
89-
Unknown { err: c_int },
66+
#[error("Decompressing this input requires a dictionary")]
67+
NeedDict,
68+
}
69+
70+
impl From<zlib_rs::InflateError> for DecompressError {
71+
fn from(value: InflateError) -> Self {
72+
match value {
73+
InflateError::NeedDict { .. } => DecompressError::NeedDict,
74+
InflateError::StreamError => DecompressError::StreamError,
75+
InflateError::DataError => DecompressError::DataError,
76+
InflateError::MemError => DecompressError::InsufficientMemory,
77+
}
78+
}
9079
}
9180

9281
/// The status returned by [`Decompress::decompress()`].
@@ -110,7 +99,7 @@ pub enum FlushDecompress {
11099
/// A typical parameter for passing to compression/decompression functions,
111100
/// this indicates that the underlying stream to decide how much data to
112101
/// accumulate before producing output in order to maximize compression.
113-
None = libz_rs_sys::Z_NO_FLUSH as isize,
102+
None = 0,
114103

115104
/// All pending output is flushed to the output buffer and the output is
116105
/// aligned on a byte boundary so that the decompressor can get all input
@@ -119,13 +108,13 @@ pub enum FlushDecompress {
119108
/// Flushing may degrade compression for some compression algorithms and so
120109
/// it should only be used when necessary. This will complete the current
121110
/// deflate block and follow it with an empty stored block.
122-
Sync = libz_rs_sys::Z_SYNC_FLUSH as isize,
111+
Sync = 2,
123112

124113
/// Pending input is processed and pending output is flushed.
125114
///
126115
/// The return value may indicate that the stream is not yet done and more
127116
/// data has yet to be processed.
128-
Finish = libz_rs_sys::Z_FINISH as isize,
117+
Finish = 4,
129118
}
130119

131120
/// non-streaming interfaces for decompression

gix-features/src/zlib/stream/deflate/mod.rs

Lines changed: 36 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::zlib::Status;
2-
use std::ffi::c_int;
2+
use zlib_rs::DeflateError;
33

44
const BUF_SIZE: usize = 4096 * 8;
55

@@ -26,10 +26,7 @@ where
2626
}
2727

2828
/// Hold all state needed for compressing data.
29-
pub struct Compress(libz_rs_sys::z_stream);
30-
31-
unsafe impl Sync for Compress {}
32-
unsafe impl Send for Compress {}
29+
pub struct Compress(zlib_rs::Deflate);
3330

3431
impl Default for Compress {
3532
fn default() -> Self {
@@ -40,72 +37,63 @@ impl Default for Compress {
4037
impl Compress {
4138
/// The number of bytes that were read from the input.
4239
pub fn total_in(&self) -> u64 {
43-
self.0.total_in as _
40+
self.0.total_in()
4441
}
4542

4643
/// The number of compressed bytes that were written to the output.
4744
pub fn total_out(&self) -> u64 {
48-
self.0.total_out as _
45+
self.0.total_out()
4946
}
5047

5148
/// Create a new instance - this allocates so should be done with care.
5249
pub fn new() -> Self {
53-
let mut this = libz_rs_sys::z_stream::default();
54-
55-
unsafe {
56-
libz_rs_sys::deflateInit_(
57-
&mut this,
58-
libz_rs_sys::Z_BEST_SPEED,
59-
libz_rs_sys::zlibVersion(),
60-
core::mem::size_of::<libz_rs_sys::z_stream>() as core::ffi::c_int,
61-
);
62-
}
63-
64-
Self(this)
50+
let inner = zlib_rs::Deflate::new(zlib_rs::c_api::Z_BEST_SPEED, true, zlib_rs::MAX_WBITS as u8);
51+
Self(inner)
6552
}
6653

6754
/// Prepare the instance for a new stream.
6855
pub fn reset(&mut self) {
69-
unsafe { libz_rs_sys::deflateReset(&mut self.0) };
56+
self.0.reset();
7057
}
7158

7259
/// Compress `input` and write compressed bytes to `output`, with `flush` controlling additional characteristics.
7360
pub fn compress(&mut self, input: &[u8], output: &mut [u8], flush: FlushCompress) -> Result<Status, CompressError> {
74-
self.0.avail_in = input.len() as _;
75-
self.0.avail_out = output.len() as _;
76-
77-
self.0.next_in = input.as_ptr();
78-
self.0.next_out = output.as_mut_ptr();
79-
80-
match unsafe { libz_rs_sys::deflate(&mut self.0, flush as _) } {
81-
libz_rs_sys::Z_OK => Ok(Status::Ok),
82-
libz_rs_sys::Z_BUF_ERROR => Ok(Status::BufError),
83-
libz_rs_sys::Z_STREAM_END => Ok(Status::StreamEnd),
84-
85-
libz_rs_sys::Z_STREAM_ERROR => Err(CompressError::StreamError),
86-
libz_rs_sys::Z_MEM_ERROR => Err(CompressError::InsufficientMemory),
87-
err => Err(CompressError::Unknown { err }),
61+
let flush = match flush {
62+
FlushCompress::None => zlib_rs::DeflateFlush::NoFlush,
63+
FlushCompress::Partial => zlib_rs::DeflateFlush::PartialFlush,
64+
FlushCompress::Sync => zlib_rs::DeflateFlush::SyncFlush,
65+
FlushCompress::Full => zlib_rs::DeflateFlush::FullFlush,
66+
FlushCompress::Finish => zlib_rs::DeflateFlush::Finish,
67+
};
68+
let status = self.0.compress(input, output, flush)?;
69+
match status {
70+
zlib_rs::Status::Ok => Ok(Status::Ok),
71+
zlib_rs::Status::BufError => Ok(Status::BufError),
72+
zlib_rs::Status::StreamEnd => Ok(Status::StreamEnd),
8873
}
8974
}
9075
}
9176

92-
impl Drop for Compress {
93-
fn drop(&mut self) {
94-
unsafe { libz_rs_sys::deflateEnd(&mut self.0) };
95-
}
96-
}
97-
9877
/// The error produced by [`Compress::compress()`].
9978
#[derive(Debug, thiserror::Error)]
100-
#[error("{msg}")]
10179
#[allow(missing_docs)]
10280
pub enum CompressError {
10381
#[error("stream error")]
10482
StreamError,
83+
#[error("The input is not a valid deflate stream.")]
84+
DataError,
10585
#[error("Not enough memory")]
10686
InsufficientMemory,
107-
#[error("An unknown error occurred: {err}")]
108-
Unknown { err: c_int },
87+
}
88+
89+
impl From<zlib_rs::DeflateError> for CompressError {
90+
fn from(value: zlib_rs::DeflateError) -> Self {
91+
match value {
92+
DeflateError::StreamError => CompressError::StreamError,
93+
DeflateError::DataError => CompressError::DataError,
94+
DeflateError::MemError => CompressError::InsufficientMemory,
95+
}
96+
}
10997
}
11098

11199
/// Values which indicate the form of flushing to be used when compressing
@@ -117,7 +105,7 @@ pub enum FlushCompress {
117105
/// A typical parameter for passing to compression/decompression functions,
118106
/// this indicates that the underlying stream to decide how much data to
119107
/// accumulate before producing output in order to maximize compression.
120-
None = libz_rs_sys::Z_NO_FLUSH as isize,
108+
None = 0,
121109

122110
/// All pending output is flushed to the output buffer, but the output is
123111
/// not aligned to a byte boundary.
@@ -127,7 +115,7 @@ pub enum FlushCompress {
127115
/// with an empty fixed codes block that is 10 bits long, and it assures
128116
/// that enough bytes are output in order for the decompressor to finish the
129117
/// block before the empty fixed code block.
130-
Partial = libz_rs_sys::Z_PARTIAL_FLUSH as isize,
118+
Partial = 1,
131119

132120
/// All pending output is flushed to the output buffer and the output is
133121
/// aligned on a byte boundary so that the decompressor can get all input
@@ -136,20 +124,20 @@ pub enum FlushCompress {
136124
/// Flushing may degrade compression for some compression algorithms and so
137125
/// it should only be used when necessary. This will complete the current
138126
/// deflate block and follow it with an empty stored block.
139-
Sync = libz_rs_sys::Z_SYNC_FLUSH as isize,
127+
Sync = 2,
140128

141129
/// All output is flushed as with `Flush::Sync` and the compression state is
142130
/// reset so decompression can restart from this point if previous
143131
/// compressed data has been damaged or if random access is desired.
144132
///
145133
/// Using this option too often can seriously degrade compression.
146-
Full = libz_rs_sys::Z_FULL_FLUSH as isize,
134+
Full = 3,
147135

148136
/// Pending input is processed and pending output is flushed.
149137
///
150138
/// The return value may indicate that the stream is not yet done and more
151139
/// data has yet to be processed.
152-
Finish = libz_rs_sys::Z_FINISH as isize,
140+
Finish = 4,
153141
}
154142

155143
mod impls {

0 commit comments

Comments
 (0)