Skip to content

Commit 8839b34

Browse files
authored
Merge pull request #64 from bgpkit/feat/gzip-backends
2 parents a21ca43 + bb25dad commit 8839b34

File tree

7 files changed

+101
-17
lines changed

7 files changed

+101
-17
lines changed

CHANGELOG.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,30 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## v0.19.1 -- 2025-10-15
6+
7+
### Changed
8+
- Gzip backend selection via feature flags:
9+
- Default feature `gz` switched to use flate2 with the `gz-zlib-rs` backend for improved performance.
10+
- New selectors and aliases:
11+
- `gz-zlib-rs` — enables `flate2/zlib-rs` (Rust, fast)
12+
- `gz-miniz` — enables `flate2/miniz_oxide` (pure Rust, most portable)
13+
- Disabled `flate2` default-features to allow explicit backend choice.
14+
15+
### Added
16+
- Criterion benchmark `benches/gzip_decompress.rs` to measure gzip decompression throughput across backends.
17+
18+
### Usage
19+
- Default (zlib-rs):
20+
- cargo build
21+
- cargo bench --bench gzip_decompress --features gz
22+
- zlib-rs:
23+
- cargo build --no-default-features --features gz-zlib-rs
24+
- cargo bench --bench gzip_decompress --no-default-features --features gz-zlib-rs
25+
- miniz_oxide (explicit):
26+
- cargo build --no-default-features --features gz-miniz
27+
- cargo bench --bench gzip_decompress --no-default-features --features gz-miniz
28+
529
## v0.19.0 -- 2025-08-31
630

731
### Breaking Changes
@@ -14,7 +38,7 @@ All notable changes to this project will be documented in this file.
1438

1539
**Migration guide:**
1640

17-
```toml
41+
```
1842
# Before (v0.18.x)
1943
oneio = { version = "0.18", features = ["lib-core", "rustls"] }
2044

Cargo.toml

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "oneio"
3-
version = "0.19.0"
3+
version = "0.19.1"
44
authors = ["Mingwei Zhang <[email protected]>"]
55
edition = "2021"
66
readme = "README.md"
@@ -30,7 +30,8 @@ reqwest = { version = "0.12", default-features = false, features = ["blocking",
3030
suppaftp = { version = "7.0", optional = true }
3131

3232
# feature: compressions
33-
flate2 = { version = "1", optional = true }
33+
# Turn off flate2 default-features so we can explicitly choose backend via features
34+
flate2 = { version = "1", optional = true, default-features = false }
3435
bzip2 = { version = "0.6.0", optional = true }
3536
lz4 = { version = "1.24", optional = true }
3637
xz2 = { version = "0.1", optional = true }
@@ -45,7 +46,7 @@ serde = { version = "1.0", optional = true }
4546
serde_json = { version = "1.0", optional = true }
4647

4748
# feature: s3
48-
rust-s3 = { version = "0.35", optional = true, default-features = false, features = [
49+
rust-s3 = { version = "0.37", optional = true, default-features = false, features = [
4950
"sync",
5051
] }
5152

@@ -75,8 +76,14 @@ https = ["http", "rustls"] # https needs http
7576
ftp = ["https", "suppaftp"] # ftp needs https
7677
s3 = ["rust-s3"]
7778

78-
# Compression features (independent)
79-
gz = ["flate2"]
79+
gz = ["gz-zlib-rs"]
80+
# internal feature to enable gzip support
81+
any_gz = []
82+
# fastest Rust impl with some unsafe code
83+
gz-zlib-rs = ["any_gz", "flate2/zlib-rs"]
84+
# slower pure safe Rust impl
85+
gz-miniz = ["any_gz", "flate2/miniz_oxide", "flate2/any_impl"]
86+
8087
bz = ["bzip2"]
8188
lz = ["lz4"]
8289
xz = ["xz2"]
@@ -111,6 +118,13 @@ tracing-subscriber = "0.3"
111118
tar = "0.4"
112119
tokio = { version = "1.0", features = ["macros", "rt"] }
113120
indicatif = "0.18"
121+
criterion = { version = "0.5", default-features = false }
122+
123+
# Benchmarks
124+
[[bench]]
125+
name = "gzip_decompress"
126+
harness = false
127+
required-features = ["any_gz"]
114128

115129
# This list only includes examples which require additional features to run. These are more in the examples' directory.
116130
[[example]]

benches/gzip_decompress.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
use std::fs::File;
2+
use std::hint::black_box;
3+
use std::io::Read;
4+
5+
use criterion::{criterion_group, criterion_main, BatchSize, Criterion, Throughput};
6+
use flate2::read::GzDecoder;
7+
8+
// Benchmark gzip decompression using flate2 with the selected backend.
9+
// To run with default (miniz_oxide) backend:
10+
// cargo bench --bench gzip_decompress --no-default-features --features gz-miniz
11+
// To run with zlib-rs backend:
12+
// cargo bench --bench gzip_decompress --no-default-features --features gz-zlib-rs
13+
// To compare, run both commands and compare Criterion reports.
14+
15+
fn load_gz_bytes() -> Vec<u8> {
16+
let mut f = File::open("tests/test_data.txt.gz").expect("missing tests/test_data.txt.gz");
17+
let mut buf = Vec::new();
18+
f.read_to_end(&mut buf).unwrap();
19+
buf
20+
}
21+
22+
fn bench_gzip_decompress(c: &mut Criterion) {
23+
let input = load_gz_bytes();
24+
25+
let mut group = c.benchmark_group("gzip_decompress");
26+
group.throughput(Throughput::Bytes(input.len() as u64));
27+
28+
group.bench_function("flate2_gz_decode", |b| {
29+
b.iter_batched(
30+
|| input.clone(),
31+
|bytes| {
32+
let reader = GzDecoder::new(bytes.as_slice());
33+
let mut out = Vec::with_capacity(128 * 1024);
34+
let mut r = reader;
35+
r.read_to_end(&mut out).unwrap();
36+
black_box(out)
37+
},
38+
BatchSize::SmallInput,
39+
)
40+
});
41+
42+
group.finish();
43+
}
44+
45+
criterion_group!(benches, bench_gzip_decompress);
46+
criterion_main!(benches);

src/oneio/compressions/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use std::io::{BufWriter, Read, Write};
1111

1212
#[cfg(feature = "bz")]
1313
pub(crate) mod bzip2;
14-
#[cfg(feature = "gz")]
14+
#[cfg(feature = "any_gz")]
1515
pub(crate) mod gzip;
1616
#[cfg(feature = "lz")]
1717
pub(crate) mod lz4;
@@ -50,7 +50,7 @@ pub(crate) fn get_compression_reader(
5050
file_suffix: &str,
5151
) -> Result<Box<dyn Read + Send>, OneIoError> {
5252
match file_suffix {
53-
#[cfg(feature = "gz")]
53+
#[cfg(feature = "any_gz")]
5454
"gz" | "gzip" | "tgz" => gzip::get_reader(raw_reader),
5555
#[cfg(feature = "bz")]
5656
"bz2" | "bz" => bzip2::get_reader(raw_reader),
@@ -97,7 +97,7 @@ pub(crate) fn get_compression_writer(
9797
file_suffix: &str,
9898
) -> Result<Box<dyn Write>, OneIoError> {
9999
match file_suffix {
100-
#[cfg(feature = "gz")]
100+
#[cfg(feature = "any_gz")]
101101
"gz" | "gzip" | "tgz" => gzip::get_writer(raw_writer),
102102
#[cfg(feature = "bz")]
103103
"bz2" | "bz" => bzip2::get_writer(raw_writer),

src/oneio/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ fn get_async_compression_reader(
552552
file_type: &str,
553553
) -> Result<Box<dyn AsyncRead + Send + Unpin>, OneIoError> {
554554
match file_type {
555-
#[cfg(all(feature = "async", feature = "gz"))]
555+
#[cfg(all(feature = "async", feature = "any_gz"))]
556556
"gz" | "gzip" => {
557557
use async_compression::tokio::bufread::GzipDecoder;
558558
use tokio::io::BufReader;
@@ -608,7 +608,7 @@ mod tests {
608608

609609
const TEST_TEXT: &str = "OneIO test file.\nThis is a test.";
610610

611-
#[cfg(feature = "gz")]
611+
#[cfg(feature = "any_gz")]
612612
#[test]
613613
fn test_progress_tracking_local() {
614614
use std::sync::{Arc, Mutex};
@@ -751,7 +751,7 @@ mod tests {
751751
}
752752

753753
// Test with compression formats that support async
754-
#[cfg(feature = "gz")]
754+
#[cfg(feature = "any_gz")]
755755
{
756756
match get_reader_async("tests/test_data.txt.gz").await {
757757
Ok(mut reader) => {

tests/async_integration.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ async fn async_read_local_plain() {
1818
assert_eq!(content, TEST_TEXT);
1919
}
2020

21-
#[cfg(feature = "gz")]
21+
#[cfg(feature = "any_gz")]
2222
#[tokio::test]
2323
async fn async_read_local_gzip() {
2424
let mut reader = oneio::get_reader_async("tests/test_data.txt.gz")
@@ -46,7 +46,7 @@ async fn async_read_http_plain() {
4646
}
4747
}
4848

49-
#[cfg(all(feature = "http", feature = "gz"))]
49+
#[cfg(all(feature = "http", feature = "any_gz"))]
5050
#[tokio::test]
5151
async fn async_read_http_gzip() {
5252
match oneio::get_reader_async("https://spaces.bgpkit.org/oneio/test_data.txt.gz").await {

tests/basic_integration.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ fn test_local_files() {
5353
test_read("tests/test_data.txt");
5454

5555
// Test gzip (default feature)
56-
#[cfg(feature = "gz")]
56+
#[cfg(feature = "any_gz")]
5757
test_read("tests/test_data.txt.gz");
5858

5959
// Test bzip2 (default feature)
@@ -66,7 +66,7 @@ fn test_writers() {
6666
// Test writing with default compression formats
6767
test_write("tests/test_write_data.txt", "tests/test_data.txt");
6868

69-
#[cfg(feature = "gz")]
69+
#[cfg(feature = "any_gz")]
7070
test_write("tests/test_write_data.txt.gz", "tests/test_data.txt.gz");
7171

7272
#[cfg(feature = "bz")]
@@ -79,7 +79,7 @@ fn test_remote_files() {
7979
// Test HTTP reading (default feature)
8080
test_read("https://spaces.bgpkit.org/oneio/test_data.txt");
8181

82-
#[cfg(feature = "gz")]
82+
#[cfg(feature = "any_gz")]
8383
test_read("https://spaces.bgpkit.org/oneio/test_data.txt.gz");
8484

8585
#[cfg(feature = "bz")]

0 commit comments

Comments
 (0)