Skip to content

Commit 752d6dc

Browse files
authored
Merge pull request #2155 from folkertdev/skip-flate2
in `gix-features`, use `libz-rs-sys` directly, skipping `flate2`
2 parents 768164a + e5a7487 commit 752d6dc

File tree

10 files changed

+285
-51
lines changed

10 files changed

+285
-51
lines changed

Cargo.lock

Lines changed: 5 additions & 5 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: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -55,22 +55,8 @@ io-pipe = ["dep:bytes"]
5555
crc32 = ["dep:crc32fast"]
5656

5757
## Enable the usage of zlib-related utilities to compress or decompress data.
58-
## This enables the `flate2` crate, and always uses the high-performance `zlib-rs` backend.
59-
## Note that the various past features for selecting zlib backends are now deprecated and do nothing.
60-
zlib = ["dep:flate2", "dep:thiserror"]
61-
## Deprecated: gix always uses zlib-rs.
62-
zlib-ng = ["zlib"]
63-
## Deprecated: gix always uses zlib-rs now. As of zlib-rs 0.5.0 (used by flate2
64-
## 1.1.1), this no longer exports C symbols # by default, so it doesn't
65-
## conflict with any other zlib library that might be loaded into the same
66-
## address space.
67-
zlib-rs = ["zlib"]
68-
## Deprecated: gix always uses zlib-rs.
69-
zlib-ng-compat = ["zlib"]
70-
## Deprecated: gix always uses zlib-rs.
71-
zlib-stock = ["zlib"]
72-
## Deprecated: gix always uses zlib-rs.
73-
zlib-rust-backend = ["zlib"]
58+
## This enables and uses the high-performance `zlib-rs` backend.
59+
zlib = ["dep:libz-rs-sys", "dep:thiserror"]
7460

7561
#! ### Other
7662

@@ -121,7 +107,7 @@ bytesize = { version = "2.0.1", optional = true }
121107
bytes = { version = "1.0.0", optional = true }
122108

123109
# zlib module
124-
flate2 = { version = "1.1.1", optional = true, default-features = false, features = ["zlib-rs"] }
110+
libz-rs-sys = { version = "0.5.2", optional = true }
125111
thiserror = { version = "2.0.0", optional = true }
126112

127113
once_cell = { version = "1.21.3", optional = true }

gix-features/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
doc = ::document_features::document_features!()
1313
)]
1414
#![cfg_attr(all(doc, feature = "document-features"), feature(doc_cfg, doc_auto_cfg))]
15-
#![deny(missing_docs, rust_2018_idioms, unsafe_code)]
15+
#![deny(rust_2018_idioms, missing_docs)]
1616

1717
///
1818
pub mod cache;

gix-features/src/zlib/mod.rs

Lines changed: 136 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,132 @@
1-
pub use flate2::{Decompress, Status};
1+
use std::ffi::c_int;
2+
3+
/// 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 {}
8+
9+
impl Default for Decompress {
10+
fn default() -> Self {
11+
Self::new()
12+
}
13+
}
14+
15+
impl Decompress {
16+
/// The amount of bytes consumed from the input so far.
17+
pub fn total_in(&self) -> u64 {
18+
self.0.total_in as _
19+
}
20+
21+
/// The amount of decompressed bytes that have been written to the output thus far.
22+
pub fn total_out(&self) -> u64 {
23+
self.0.total_out as _
24+
}
25+
26+
/// Create a new instance. Note that it allocates in various ways and thus should be re-used.
27+
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)
39+
}
40+
41+
/// Reset the state to allow handling a new stream.
42+
pub fn reset(&mut self) {
43+
unsafe { libz_rs_sys::inflateReset(&mut self.0) };
44+
}
45+
46+
/// Decompress `input` and write all decompressed bytes into `output`, with `flush` defining some details about this.
47+
pub fn decompress(
48+
&mut self,
49+
input: &[u8],
50+
output: &mut [u8],
51+
flush: FlushDecompress,
52+
) -> 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 }),
68+
}
69+
}
70+
}
71+
72+
impl Drop for Decompress {
73+
fn drop(&mut self) {
74+
unsafe { libz_rs_sys::inflateEnd(&mut self.0) };
75+
}
76+
}
77+
78+
/// The error produced by [`Decompress::decompress()`].
79+
#[derive(Debug, thiserror::Error)]
80+
#[allow(missing_docs)]
81+
pub enum DecompressError {
82+
#[error("stream error")]
83+
StreamError,
84+
#[error("Not enough memory")]
85+
InsufficientMemory,
86+
#[error("Invalid input data")]
87+
DataError,
88+
#[error("An unknown error occurred: {err}")]
89+
Unknown { err: c_int },
90+
}
91+
92+
/// The status returned by [`Decompress::decompress()`].
93+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94+
pub enum Status {
95+
/// The decompress operation went well. Not to be confused with `StreamEnd`, so one can continue
96+
/// the decompression.
97+
Ok,
98+
/// An error occurred when decompression.
99+
BufError,
100+
/// The stream was fully decompressed.
101+
StreamEnd,
102+
}
103+
104+
/// Values which indicate the form of flushing to be used when
105+
/// decompressing in-memory data.
106+
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
107+
#[non_exhaustive]
108+
#[allow(clippy::unnecessary_cast)]
109+
pub enum FlushDecompress {
110+
/// A typical parameter for passing to compression/decompression functions,
111+
/// this indicates that the underlying stream to decide how much data to
112+
/// accumulate before producing output in order to maximize compression.
113+
None = libz_rs_sys::Z_NO_FLUSH as isize,
114+
115+
/// All pending output is flushed to the output buffer and the output is
116+
/// aligned on a byte boundary so that the decompressor can get all input
117+
/// data available so far.
118+
///
119+
/// Flushing may degrade compression for some compression algorithms and so
120+
/// it should only be used when necessary. This will complete the current
121+
/// deflate block and follow it with an empty stored block.
122+
Sync = libz_rs_sys::Z_SYNC_FLUSH as isize,
123+
124+
/// Pending input is processed and pending output is flushed.
125+
///
126+
/// The return value may indicate that the stream is not yet done and more
127+
/// data has yet to be processed.
128+
Finish = libz_rs_sys::Z_FINISH as isize,
129+
}
2130

3131
/// non-streaming interfaces for decompression
4132
pub mod inflate {
@@ -8,33 +136,26 @@ pub mod inflate {
8136
pub enum Error {
9137
#[error("Could not write all bytes when decompressing content")]
10138
WriteInflated(#[from] std::io::Error),
11-
#[error("Could not decode zip stream, status was '{0:?}'")]
12-
Inflate(#[from] flate2::DecompressError),
139+
#[error("Could not decode zip stream, status was '{0}'")]
140+
Inflate(#[from] super::DecompressError),
13141
#[error("The zlib status indicated an error, status was '{0:?}'")]
14-
Status(flate2::Status),
142+
Status(super::Status),
15143
}
16144
}
17145

18146
/// Decompress a few bytes of a zlib stream without allocation
147+
#[derive(Default)]
19148
pub struct Inflate {
20149
/// The actual decompressor doing all the work.
21150
pub state: Decompress,
22151
}
23152

24-
impl Default for Inflate {
25-
fn default() -> Self {
26-
Inflate {
27-
state: Decompress::new(true),
28-
}
29-
}
30-
}
31-
32153
impl Inflate {
33154
/// Run the decompressor exactly once. Cannot be run multiple times
34-
pub fn once(&mut self, input: &[u8], out: &mut [u8]) -> Result<(flate2::Status, usize, usize), inflate::Error> {
155+
pub fn once(&mut self, input: &[u8], out: &mut [u8]) -> Result<(Status, usize, usize), inflate::Error> {
35156
let before_in = self.state.total_in();
36157
let before_out = self.state.total_out();
37-
let status = self.state.decompress(input, out, flate2::FlushDecompress::None)?;
158+
let status = self.state.decompress(input, out, FlushDecompress::None)?;
38159
Ok((
39160
status,
40161
(self.state.total_in() - before_in) as usize,
@@ -44,7 +165,7 @@ impl Inflate {
44165

45166
/// Ready this instance for decoding another data stream.
46167
pub fn reset(&mut self) {
47-
self.state.reset(true);
168+
self.state.reset();
48169
}
49170
}
50171

0 commit comments

Comments
 (0)