Skip to content

Commit ca9cd30

Browse files
authored
Merge pull request #331 from arpankapoor/lz4
feat: add lz4 support
2 parents beb5c6b + 49375b4 commit ca9cd30

File tree

16 files changed

+625
-3
lines changed

16 files changed

+625
-3
lines changed

Cargo.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@ rustdoc-args = ["--cfg", "docsrs"]
2020
# groups
2121
all = ["all-implementations", "all-algorithms"]
2222
all-implementations = ["futures-io", "tokio"]
23-
all-algorithms = ["brotli", "bzip2", "deflate", "gzip", "lzma", "xz", "zlib", "zstd", "deflate64"]
23+
all-algorithms = ["brotli", "bzip2", "deflate", "gzip", "lz4", "lzma", "xz", "zlib", "zstd", "deflate64"]
2424

2525
# algorithms
2626
deflate = ["flate2"]
2727
gzip = ["flate2"]
28+
lz4 = ["dep:lz4"]
2829
lzma = ["dep:liblzma"]
2930
xz = ["lzma"]
3031
xz2 = ["xz"]
@@ -40,6 +41,7 @@ flate2 = { version = "1.0.13", optional = true }
4041
futures-core = { version = "0.3", default-features = false }
4142
futures-io = { version = "0.3", default-features = false, features = ["std"], optional = true }
4243
libzstd = { package = "zstd", version = "0.13.1", optional = true, default-features = false }
44+
lz4 = { version = "1.28.1", optional = true }
4345
memchr = "2"
4446
pin-project-lite = "0.2"
4547
tokio = { version = "1.24.2", optional = true, default-features = false }
@@ -74,6 +76,10 @@ required-features = ["deflate"]
7476
name = "gzip"
7577
required-features = ["gzip"]
7678

79+
[[test]]
80+
name = "lz4"
81+
required-features = ["lz4"]
82+
7783
[[test]]
7884
name = "lzma"
7985
required-features = ["lzma"]

src/codec/lz4/decoder.rs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
use std::io::Result;
2+
3+
use lz4::liblz4::{
4+
check_error, LZ4FDecompressionContext, LZ4F_createDecompressionContext, LZ4F_decompress,
5+
LZ4F_freeDecompressionContext, LZ4F_resetDecompressionContext, LZ4F_VERSION,
6+
};
7+
8+
use crate::{codec::Decode, unshared::Unshared, util::PartialBuffer};
9+
10+
#[derive(Debug)]
11+
struct DecoderContext {
12+
ctx: LZ4FDecompressionContext,
13+
}
14+
15+
#[derive(Debug)]
16+
pub struct Lz4Decoder {
17+
ctx: Unshared<DecoderContext>,
18+
}
19+
20+
impl DecoderContext {
21+
fn new() -> Result<Self> {
22+
let mut context = LZ4FDecompressionContext(core::ptr::null_mut());
23+
check_error(unsafe { LZ4F_createDecompressionContext(&mut context, LZ4F_VERSION) })?;
24+
Ok(Self { ctx: context })
25+
}
26+
}
27+
28+
impl Drop for DecoderContext {
29+
fn drop(&mut self) {
30+
unsafe { LZ4F_freeDecompressionContext(self.ctx) };
31+
}
32+
}
33+
34+
impl Lz4Decoder {
35+
pub(crate) fn new() -> Self {
36+
Self {
37+
ctx: Unshared::new(DecoderContext::new().unwrap()),
38+
}
39+
}
40+
}
41+
42+
impl Decode for Lz4Decoder {
43+
fn reinit(&mut self) -> Result<()> {
44+
unsafe { LZ4F_resetDecompressionContext(self.ctx.get_mut().ctx) };
45+
Ok(())
46+
}
47+
48+
fn decode(
49+
&mut self,
50+
input: &mut PartialBuffer<impl AsRef<[u8]>>,
51+
output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
52+
) -> Result<bool> {
53+
let mut output_size = output.unwritten().len();
54+
let mut input_size = input.unwritten().len();
55+
let remaining = unsafe {
56+
check_error(LZ4F_decompress(
57+
self.ctx.get_mut().ctx,
58+
output.unwritten_mut().as_mut_ptr(),
59+
&mut output_size,
60+
input.unwritten().as_ptr(),
61+
&mut input_size,
62+
core::ptr::null(),
63+
))?
64+
};
65+
input.advance(input_size);
66+
output.advance(output_size);
67+
Ok(remaining == 0)
68+
}
69+
70+
fn flush(
71+
&mut self,
72+
output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
73+
) -> Result<bool> {
74+
self.decode(&mut PartialBuffer::new(&[][..]), output)?;
75+
76+
loop {
77+
let old_len = output.written().len();
78+
self.decode(&mut PartialBuffer::new(&[][..]), output)?;
79+
if output.written().len() == old_len {
80+
break;
81+
}
82+
}
83+
84+
Ok(!output.unwritten().is_empty())
85+
}
86+
87+
fn finish(
88+
&mut self,
89+
output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
90+
) -> Result<bool> {
91+
self.flush(output)
92+
}
93+
}

0 commit comments

Comments
 (0)