Skip to content

Commit 9967f2e

Browse files
authored
Merge pull request #285 from quantatic/main
Enable customizing Zstd decoding parameters.
2 parents 7c2d530 + c0c6559 commit 9967f2e

File tree

6 files changed

+91
-1
lines changed

6 files changed

+91
-1
lines changed

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ bzip2 = { version = "0.4.4", optional = true }
3737
flate2 = { version = "1.0.13", optional = true }
3838
futures-core = { version = "0.3", default-features = false }
3939
futures-io = { version = "0.3", default-features = false, features = ["std"], optional = true }
40-
libzstd = { package = "zstd", version = "0.13", optional = true, default-features = false }
40+
libzstd = { package = "zstd", version = "0.13.1", optional = true, default-features = false }
4141
memchr = "2"
4242
pin-project-lite = "0.2"
4343
tokio = { version = "1.24.2", optional = true, default-features = false }
@@ -92,6 +92,10 @@ required-features = ["zstd"]
9292
name = "zstd-dict"
9393
required-features = ["zstd", "tokio"]
9494

95+
[[test]]
96+
name = "zstd-window-size"
97+
required-features = ["zstd", "tokio"]
98+
9599
[[example]]
96100
name = "zlib_tokio_write"
97101
required-features = ["zlib", "tokio"]

src/codec/zstd/decoder.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ impl ZstdDecoder {
1616
}
1717
}
1818

19+
pub(crate) fn new_with_params(params: &[crate::zstd::DParameter]) -> Self {
20+
let mut decoder = Decoder::new().unwrap();
21+
for param in params {
22+
decoder.set_parameter(param.as_zstd()).unwrap();
23+
}
24+
Self {
25+
decoder: Unshared::new(decoder),
26+
}
27+
}
28+
1929
pub(crate) fn new_with_dict(dictionary: &[u8]) -> io::Result<Self> {
2030
let mut decoder = Decoder::with_dictionary(dictionary)?;
2131
Ok(Self {

src/macros.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,17 @@ macro_rules! algos {
198198
}
199199
}
200200
{ @dec
201+
/// Creates a new decoder, using the specified parameters, which will read compressed
202+
/// data from the given stream and emit a decompressed stream.
203+
pub fn with_params(inner: $inner, params: &[crate::zstd::DParameter]) -> Self {
204+
Self {
205+
inner: crate::$($mod::)+generic::Decoder::new(
206+
inner,
207+
crate::codec::ZstdDecoder::new_with_params(params),
208+
),
209+
}
210+
}
211+
201212
/// Creates a new decoder, using the specified compression level and pre-trained
202213
/// dictionary, which will read compressed data from the given stream and emit an
203214
/// uncompressed stream.

src/zstd.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! This module contains zstd-specific types for async-compression.
22
33
use libzstd::stream::raw::CParameter::*;
4+
use libzstd::stream::raw::DParameter::*;
45

56
/// A compression parameter for zstd. This is a stable wrapper around zstd's own `CParameter`
67
/// type, to abstract over different versions of the zstd library.
@@ -110,3 +111,22 @@ impl CParameter {
110111
self.0
111112
}
112113
}
114+
115+
/// A decompression parameter for zstd. This is a stable wrapper around zstd's own `DParameter`
116+
/// type, to abstract over different versions of the zstd library.
117+
///
118+
/// See the [zstd documentation](https://facebook.github.io/zstd/zstd_manual.html) for more
119+
/// information on these parameters.
120+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
121+
pub struct DParameter(libzstd::stream::raw::DParameter);
122+
123+
impl DParameter {
124+
/// Maximum window size in bytes (as a power of two)
125+
pub fn window_log_max(value: u32) -> Self {
126+
Self(WindowLogMax(value))
127+
}
128+
129+
pub(crate) fn as_zstd(&self) -> libzstd::stream::raw::DParameter {
130+
self.0
131+
}
132+
}
3.29 KB
Binary file not shown.

tests/zstd-window-size.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#![cfg(not(windows))]
2+
3+
use async_compression::zstd::DParameter;
4+
use tokio::io::AsyncWriteExt as _;
5+
6+
#[tokio::test]
7+
async fn zstd_decode_large_window_size_default() {
8+
let compressed = include_bytes!("./artifacts/long-window-size-lib.rs.zst");
9+
10+
// Default decoder should throw with an error, window size maximum is too low.
11+
let mut decoder = async_compression::tokio::write::ZstdDecoder::new(Vec::new());
12+
decoder.write_all(compressed).await.unwrap_err();
13+
}
14+
15+
#[tokio::test]
16+
async fn zstd_decode_large_window_size_explicit_small_window_size() {
17+
let compressed = include_bytes!("./artifacts/long-window-size-lib.rs.zst");
18+
19+
// Short window decoder should throw with an error, window size maximum is too low.
20+
let mut decoder = async_compression::tokio::write::ZstdDecoder::with_params(
21+
Vec::new(),
22+
&[DParameter::window_log_max(16)],
23+
);
24+
decoder.write_all(compressed).await.unwrap_err();
25+
}
26+
27+
#[tokio::test]
28+
async fn zstd_decode_large_window_size_explicit_large_window_size() {
29+
let compressed = include_bytes!("./artifacts/long-window-size-lib.rs.zst");
30+
let source = include_bytes!("./artifacts/lib.rs");
31+
32+
// Long window decoder should succeed as the window size is large enough to decompress the given input.
33+
let mut long_window_size_decoder = async_compression::tokio::write::ZstdDecoder::with_params(
34+
Vec::new(),
35+
&[DParameter::window_log_max(31)],
36+
);
37+
// Long window size decoder should successfully decode the given input data.
38+
long_window_size_decoder
39+
.write_all(compressed)
40+
.await
41+
.unwrap();
42+
long_window_size_decoder.shutdown().await.unwrap();
43+
44+
assert_eq!(long_window_size_decoder.into_inner(), source);
45+
}

0 commit comments

Comments
 (0)