Skip to content

Commit

Permalink
Merge pull request #108 from Badel2/lz4
Browse files Browse the repository at this point in the history
Implement LZ4 decompression for 1.20.5+
  • Loading branch information
owengage committed May 12, 2024
2 parents 08e7abb + 60d8c63 commit ad0e8d8
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 6 deletions.
33 changes: 33 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions fastanvil/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ license = "MIT OR Apache-2.0"
[dependencies]
fastnbt = { path = "../fastnbt", version = "2" }
flate2 = "1.0"
lz4-java-wrc = "0.2.0"
num_enum = "0.5"
byteorder = "1.3"
bit_field = "0.10"
Expand Down
49 changes: 43 additions & 6 deletions fastanvil/src/region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,11 @@ where
self.read_compressed_chunk(x, z, &mut buf)?;
Ok(buf)
}
CompressionScheme::Lz4 => {
let mut decoder = Lz4DecoderWrapper::new(vec![]);
self.read_compressed_chunk(x, z, &mut decoder)?;
Ok(decoder.finish()?)
}
})
.transpose()
}
Expand Down Expand Up @@ -285,12 +290,7 @@ where
// size written to disk includes the byte representing the compression
// scheme, so +1.
c.write_u32::<BigEndian>(compressed_chunk_size + 1).unwrap();
c.write_u8(match scheme {
CompressionScheme::Gzip => 1,
CompressionScheme::Zlib => 2,
CompressionScheme::Uncompressed => 3,
})
.unwrap();
c.write_u8(scheme as u8).unwrap();

buf
}
Expand Down Expand Up @@ -454,6 +454,7 @@ pub enum CompressionScheme {
Gzip = 1,
Zlib = 2,
Uncompressed = 3,
Lz4 = 4,
}

pub struct RegionIter<'a, S>
Expand Down Expand Up @@ -570,3 +571,39 @@ impl ChunkMeta {
})
}
}

/// Wrapper type used to decompress a LZ4 chunk, needed because `lz4_java_wrc::Lz4BlockInput`
/// expects a reader as input, while `self.read_compressed_chunk` expects a decoder that can be
/// written to. The solution is to use an intermediate buffer, and decompress on the call to
/// `.finish()`.
pub struct Lz4DecoderWrapper<W: Write> {
buf: Vec<u8>,
write_to: W,
}

impl<W: Write> Lz4DecoderWrapper<W> {
pub fn new(write_to: W) -> Self {
Self {
buf: vec![],
write_to,
}
}

pub fn finish(mut self) -> Result<W> {
let mut decoder = lz4_java_wrc::Lz4BlockInput::new(Cursor::new(self.buf));
io::copy(&mut decoder, &mut self.write_to)?;

Ok(self.write_to)
}
}

impl<W: Write> Write for Lz4DecoderWrapper<W> {
fn write(&mut self, data: &[u8]) -> std::io::Result<usize> {
self.buf.extend_from_slice(data);
Ok(data.len())
}

fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}

0 comments on commit ad0e8d8

Please sign in to comment.