use crate::Result;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Algo {
Gzip,
Zlib,
Xz,
Lzma,
Lz4,
Zstd,
Lzo,
}
impl Algo {
pub fn name(self) -> &'static str {
match self {
Self::Gzip => "gzip",
Self::Zlib => "zlib",
Self::Xz => "xz",
Self::Lzma => "lzma",
Self::Lz4 => "lz4",
Self::Zstd => "zstd",
Self::Lzo => "lzo",
}
}
pub fn enabled(self) -> bool {
match self {
Self::Gzip | Self::Zlib => cfg!(feature = "gzip"),
Self::Xz => cfg!(feature = "xz"),
Self::Lzma => cfg!(feature = "lzma"),
Self::Lz4 => cfg!(feature = "lz4"),
Self::Zstd => cfg!(feature = "zstd"),
Self::Lzo => cfg!(feature = "lzo"),
}
}
pub fn from_extension(path: &std::path::Path) -> Option<Self> {
let s = path.to_string_lossy();
let lower = s.to_ascii_lowercase();
if lower.ends_with(".gz") || lower.ends_with(".tgz") {
Some(Self::Gzip)
} else if lower.ends_with(".xz") || lower.ends_with(".txz") {
Some(Self::Xz)
} else if lower.ends_with(".lzma") {
Some(Self::Lzma)
} else if lower.ends_with(".lz4") {
Some(Self::Lz4)
} else if lower.ends_with(".zst") || lower.ends_with(".zstd") {
Some(Self::Zstd)
} else if lower.ends_with(".lzo") {
Some(Self::Lzo)
} else {
None
}
}
}
pub fn detect_magic(prefix: &[u8]) -> Option<Algo> {
if prefix.len() >= 2 && prefix[0] == 0x1F && prefix[1] == 0x8B {
return Some(Algo::Gzip);
}
if prefix.len() >= 6 && &prefix[0..6] == b"\xfd7zXZ\x00" {
return Some(Algo::Xz);
}
if prefix.len() >= 4 && &prefix[0..4] == b"\x28\xb5\x2f\xfd" {
return Some(Algo::Zstd);
}
if prefix.len() >= 4 && &prefix[0..4] == b"\x04\x22\x4d\x18" {
return Some(Algo::Lz4);
}
if prefix.len() >= 3 && prefix[0] == 0x5D && prefix[1] == 0x00 && prefix[2] == 0x00 {
return Some(Algo::Lzma);
}
None
}
pub fn decompress(algo: Algo, compressed: &[u8], max_out: usize) -> Result<Vec<u8>> {
match algo {
Algo::Gzip => decompress_gzip(compressed, max_out),
Algo::Zlib => decompress_zlib(compressed, max_out),
Algo::Xz => decompress_xz(compressed, max_out),
Algo::Lzma => decompress_lzma(compressed, max_out),
Algo::Lz4 => decompress_lz4(compressed, max_out),
Algo::Zstd => decompress_zstd(compressed, max_out),
Algo::Lzo => decompress_lzo(compressed, max_out),
}
}
pub fn compress(algo: Algo, plain: &[u8]) -> Result<Vec<u8>> {
match algo {
Algo::Gzip => compress_gzip(plain),
Algo::Zlib => compress_zlib(plain),
Algo::Xz => compress_xz(plain),
Algo::Lzma => compress_lzma(plain),
Algo::Lz4 => compress_lz4(plain),
Algo::Zstd => compress_zstd(plain),
Algo::Lzo => compress_lzo(plain),
}
}
pub fn make_reader<'a, R: std::io::Read + 'a>(
algo: Algo,
reader: R,
) -> Result<Box<dyn std::io::Read + 'a>> {
match algo {
Algo::Gzip => make_reader_gzip(reader),
Algo::Zlib => make_reader_zlib(reader),
Algo::Xz => make_reader_xz(reader),
Algo::Lzma => make_reader_lzma(reader),
Algo::Lz4 => make_reader_lz4(reader),
Algo::Zstd => make_reader_zstd(reader),
Algo::Lzo => make_reader_lzo(reader),
}
}
pub fn make_writer<'a, W: std::io::Write + 'a>(
algo: Algo,
writer: W,
) -> Result<Box<dyn std::io::Write + 'a>> {
match algo {
Algo::Gzip => make_writer_gzip(writer),
Algo::Zlib => make_writer_zlib(writer),
Algo::Xz => make_writer_xz(writer),
Algo::Lzma => make_writer_lzma(writer),
Algo::Lz4 => make_writer_lz4(writer),
Algo::Zstd => make_writer_zstd(writer),
Algo::Lzo => make_writer_lzo(writer),
}
}
#[allow(dead_code)]
fn disabled(algo: Algo) -> crate::Error {
crate::Error::Unsupported(format!(
"{} support is disabled — rebuild fstool with `--features {}`",
algo.name(),
algo.name()
))
}
fn cap_check(buf: &[u8], max_out: usize) -> Result<()> {
if buf.len() > max_out {
return Err(crate::Error::InvalidImage(format!(
"compression: decoded payload {} > cap {max_out}",
buf.len()
)));
}
Ok(())
}
#[cfg(feature = "gzip")]
fn decompress_gzip(src: &[u8], max_out: usize) -> Result<Vec<u8>> {
use std::io::Read;
let dec = flate2::read::GzDecoder::new(src);
let mut out = Vec::new();
dec.take(max_out as u64 + 1)
.read_to_end(&mut out)
.map_err(|e| crate::Error::InvalidImage(format!("gzip decode failed: {e}")))?;
cap_check(&out, max_out)?;
Ok(out)
}
#[cfg(not(feature = "gzip"))]
fn decompress_gzip(_src: &[u8], _max_out: usize) -> Result<Vec<u8>> {
Err(disabled(Algo::Gzip))
}
#[cfg(feature = "gzip")]
fn compress_gzip(plain: &[u8]) -> Result<Vec<u8>> {
use std::io::Write;
let mut enc = flate2::write::GzEncoder::new(Vec::new(), flate2::Compression::default());
enc.write_all(plain)
.map_err(|e| crate::Error::Io(std::io::Error::other(format!("gzip encode failed: {e}"))))?;
enc.finish()
.map_err(|e| crate::Error::Io(std::io::Error::other(format!("gzip encode failed: {e}"))))
}
#[cfg(not(feature = "gzip"))]
fn compress_gzip(_plain: &[u8]) -> Result<Vec<u8>> {
Err(disabled(Algo::Gzip))
}
#[cfg(feature = "gzip")]
fn make_reader_gzip<'a, R: std::io::Read + 'a>(r: R) -> Result<Box<dyn std::io::Read + 'a>> {
Ok(Box::new(flate2::read::GzDecoder::new(r)))
}
#[cfg(not(feature = "gzip"))]
fn make_reader_gzip<'a, R: std::io::Read + 'a>(_r: R) -> Result<Box<dyn std::io::Read + 'a>> {
Err(disabled(Algo::Gzip))
}
#[cfg(feature = "gzip")]
fn make_writer_gzip<'a, W: std::io::Write + 'a>(w: W) -> Result<Box<dyn std::io::Write + 'a>> {
Ok(Box::new(flate2::write::GzEncoder::new(
w,
flate2::Compression::default(),
)))
}
#[cfg(not(feature = "gzip"))]
fn make_writer_gzip<'a, W: std::io::Write + 'a>(_w: W) -> Result<Box<dyn std::io::Write + 'a>> {
Err(disabled(Algo::Gzip))
}
#[cfg(feature = "gzip")]
fn decompress_zlib(src: &[u8], max_out: usize) -> Result<Vec<u8>> {
use std::io::Read;
let dec = flate2::read::ZlibDecoder::new(src);
let mut out = Vec::new();
dec.take(max_out as u64 + 1)
.read_to_end(&mut out)
.map_err(|e| crate::Error::InvalidImage(format!("zlib decode failed: {e}")))?;
cap_check(&out, max_out)?;
Ok(out)
}
#[cfg(not(feature = "gzip"))]
fn decompress_zlib(_src: &[u8], _max_out: usize) -> Result<Vec<u8>> {
Err(disabled(Algo::Zlib))
}
#[cfg(feature = "gzip")]
fn compress_zlib(plain: &[u8]) -> Result<Vec<u8>> {
use std::io::Write;
let mut enc = flate2::write::ZlibEncoder::new(Vec::new(), flate2::Compression::default());
enc.write_all(plain)
.map_err(|e| crate::Error::Io(std::io::Error::other(format!("zlib encode failed: {e}"))))?;
enc.finish()
.map_err(|e| crate::Error::Io(std::io::Error::other(format!("zlib encode failed: {e}"))))
}
#[cfg(not(feature = "gzip"))]
fn compress_zlib(_plain: &[u8]) -> Result<Vec<u8>> {
Err(disabled(Algo::Zlib))
}
#[cfg(feature = "gzip")]
fn make_reader_zlib<'a, R: std::io::Read + 'a>(r: R) -> Result<Box<dyn std::io::Read + 'a>> {
Ok(Box::new(flate2::read::ZlibDecoder::new(r)))
}
#[cfg(not(feature = "gzip"))]
fn make_reader_zlib<'a, R: std::io::Read + 'a>(_r: R) -> Result<Box<dyn std::io::Read + 'a>> {
Err(disabled(Algo::Zlib))
}
#[cfg(feature = "gzip")]
fn make_writer_zlib<'a, W: std::io::Write + 'a>(w: W) -> Result<Box<dyn std::io::Write + 'a>> {
Ok(Box::new(flate2::write::ZlibEncoder::new(
w,
flate2::Compression::default(),
)))
}
#[cfg(not(feature = "gzip"))]
fn make_writer_zlib<'a, W: std::io::Write + 'a>(_w: W) -> Result<Box<dyn std::io::Write + 'a>> {
Err(disabled(Algo::Zlib))
}
#[cfg(feature = "xz")]
fn decompress_xz(src: &[u8], max_out: usize) -> Result<Vec<u8>> {
let mut input = std::io::BufReader::new(src);
let mut out = Vec::new();
lzma_rs::xz_decompress(&mut input, &mut out)
.map_err(|e| crate::Error::InvalidImage(format!("xz decode failed: {e}")))?;
cap_check(&out, max_out)?;
Ok(out)
}
#[cfg(not(feature = "xz"))]
fn decompress_xz(_src: &[u8], _max_out: usize) -> Result<Vec<u8>> {
Err(disabled(Algo::Xz))
}
#[cfg(feature = "xz")]
fn compress_xz(plain: &[u8]) -> Result<Vec<u8>> {
let mut input = std::io::BufReader::new(plain);
let mut out = Vec::new();
lzma_rs::xz_compress(&mut input, &mut out)
.map_err(|e| crate::Error::Io(std::io::Error::other(format!("xz encode failed: {e}"))))?;
Ok(out)
}
#[cfg(not(feature = "xz"))]
fn compress_xz(_plain: &[u8]) -> Result<Vec<u8>> {
Err(disabled(Algo::Xz))
}
#[cfg(feature = "xz")]
fn make_reader_xz<'a, R: std::io::Read + 'a>(r: R) -> Result<Box<dyn std::io::Read + 'a>> {
Ok(Box::new(stream::DecoderAdapter::new_xz(r)))
}
#[cfg(not(feature = "xz"))]
fn make_reader_xz<'a, R: std::io::Read + 'a>(_r: R) -> Result<Box<dyn std::io::Read + 'a>> {
Err(disabled(Algo::Xz))
}
#[cfg(feature = "xz")]
fn make_writer_xz<'a, W: std::io::Write + 'a>(w: W) -> Result<Box<dyn std::io::Write + 'a>> {
Ok(Box::new(stream::EncoderAdapter::new_xz(w)))
}
#[cfg(not(feature = "xz"))]
fn make_writer_xz<'a, W: std::io::Write + 'a>(_w: W) -> Result<Box<dyn std::io::Write + 'a>> {
Err(disabled(Algo::Xz))
}
#[cfg(feature = "lzma")]
fn decompress_lzma(src: &[u8], max_out: usize) -> Result<Vec<u8>> {
let mut input = std::io::BufReader::new(src);
let mut out = Vec::new();
lzma_rs::lzma_decompress(&mut input, &mut out)
.map_err(|e| crate::Error::InvalidImage(format!("lzma decode failed: {e}")))?;
cap_check(&out, max_out)?;
Ok(out)
}
#[cfg(not(feature = "lzma"))]
fn decompress_lzma(_src: &[u8], _max_out: usize) -> Result<Vec<u8>> {
Err(disabled(Algo::Lzma))
}
#[cfg(feature = "lzma")]
fn compress_lzma(plain: &[u8]) -> Result<Vec<u8>> {
let mut input = std::io::BufReader::new(plain);
let mut out = Vec::new();
lzma_rs::lzma_compress(&mut input, &mut out)
.map_err(|e| crate::Error::Io(std::io::Error::other(format!("lzma encode failed: {e}"))))?;
Ok(out)
}
#[cfg(not(feature = "lzma"))]
fn compress_lzma(_plain: &[u8]) -> Result<Vec<u8>> {
Err(disabled(Algo::Lzma))
}
#[cfg(feature = "lzma")]
fn make_reader_lzma<'a, R: std::io::Read + 'a>(r: R) -> Result<Box<dyn std::io::Read + 'a>> {
Ok(Box::new(stream::DecoderAdapter::new_lzma(r)))
}
#[cfg(not(feature = "lzma"))]
fn make_reader_lzma<'a, R: std::io::Read + 'a>(_r: R) -> Result<Box<dyn std::io::Read + 'a>> {
Err(disabled(Algo::Lzma))
}
#[cfg(feature = "lzma")]
fn make_writer_lzma<'a, W: std::io::Write + 'a>(w: W) -> Result<Box<dyn std::io::Write + 'a>> {
Ok(Box::new(stream::EncoderAdapter::new_lzma(w)))
}
#[cfg(not(feature = "lzma"))]
fn make_writer_lzma<'a, W: std::io::Write + 'a>(_w: W) -> Result<Box<dyn std::io::Write + 'a>> {
Err(disabled(Algo::Lzma))
}
#[cfg(feature = "lz4")]
fn decompress_lz4(src: &[u8], max_out: usize) -> Result<Vec<u8>> {
let out = lz4_flex::block::decompress(src, max_out)
.map_err(|e| crate::Error::InvalidImage(format!("lz4 decode failed: {e}")))?;
cap_check(&out, max_out)?;
Ok(out)
}
#[cfg(not(feature = "lz4"))]
fn decompress_lz4(_src: &[u8], _max_out: usize) -> Result<Vec<u8>> {
Err(disabled(Algo::Lz4))
}
#[cfg(feature = "lz4")]
fn compress_lz4(plain: &[u8]) -> Result<Vec<u8>> {
Ok(lz4_flex::block::compress(plain))
}
#[cfg(not(feature = "lz4"))]
fn compress_lz4(_plain: &[u8]) -> Result<Vec<u8>> {
Err(disabled(Algo::Lz4))
}
#[cfg(feature = "lz4")]
fn make_reader_lz4<'a, R: std::io::Read + 'a>(r: R) -> Result<Box<dyn std::io::Read + 'a>> {
Ok(Box::new(lz4_flex::frame::FrameDecoder::new(r)))
}
#[cfg(not(feature = "lz4"))]
fn make_reader_lz4<'a, R: std::io::Read + 'a>(_r: R) -> Result<Box<dyn std::io::Read + 'a>> {
Err(disabled(Algo::Lz4))
}
#[cfg(feature = "lz4")]
fn make_writer_lz4<'a, W: std::io::Write + 'a>(w: W) -> Result<Box<dyn std::io::Write + 'a>> {
Ok(Box::new(lz4_flex::frame::FrameEncoder::new(w)))
}
#[cfg(not(feature = "lz4"))]
fn make_writer_lz4<'a, W: std::io::Write + 'a>(_w: W) -> Result<Box<dyn std::io::Write + 'a>> {
Err(disabled(Algo::Lz4))
}
#[cfg(feature = "zstd")]
fn decompress_zstd(src: &[u8], max_out: usize) -> Result<Vec<u8>> {
let out = zstd::stream::decode_all(src)
.map_err(|e| crate::Error::InvalidImage(format!("zstd decode failed: {e}")))?;
cap_check(&out, max_out)?;
Ok(out)
}
#[cfg(not(feature = "zstd"))]
fn decompress_zstd(_src: &[u8], _max_out: usize) -> Result<Vec<u8>> {
Err(disabled(Algo::Zstd))
}
#[cfg(feature = "zstd")]
fn compress_zstd(plain: &[u8]) -> Result<Vec<u8>> {
zstd::stream::encode_all(plain, 3)
.map_err(|e| crate::Error::Io(std::io::Error::other(format!("zstd encode failed: {e}"))))
}
#[cfg(not(feature = "zstd"))]
fn compress_zstd(_plain: &[u8]) -> Result<Vec<u8>> {
Err(disabled(Algo::Zstd))
}
#[cfg(feature = "zstd")]
fn make_reader_zstd<'a, R: std::io::Read + 'a>(r: R) -> Result<Box<dyn std::io::Read + 'a>> {
let dec = zstd::stream::read::Decoder::new(r)
.map_err(|e| crate::Error::InvalidImage(format!("zstd reader init: {e}")))?;
Ok(Box::new(dec))
}
#[cfg(not(feature = "zstd"))]
fn make_reader_zstd<'a, R: std::io::Read + 'a>(_r: R) -> Result<Box<dyn std::io::Read + 'a>> {
Err(disabled(Algo::Zstd))
}
#[cfg(feature = "zstd")]
fn make_writer_zstd<'a, W: std::io::Write + 'a>(w: W) -> Result<Box<dyn std::io::Write + 'a>> {
let enc = zstd::stream::write::Encoder::new(w, 3)
.map_err(|e| crate::Error::Io(std::io::Error::other(format!("zstd writer init: {e}"))))?
.auto_finish();
Ok(Box::new(enc))
}
#[cfg(not(feature = "zstd"))]
fn make_writer_zstd<'a, W: std::io::Write + 'a>(_w: W) -> Result<Box<dyn std::io::Write + 'a>> {
Err(disabled(Algo::Zstd))
}
#[cfg(feature = "lzo")]
fn decompress_lzo(src: &[u8], max_out: usize) -> Result<Vec<u8>> {
let lzo = minilzo_rs::LZO::init()
.map_err(|e| crate::Error::InvalidImage(format!("lzo init: {e:?}")))?;
let out = lzo
.decompress(src, max_out)
.map_err(|e| crate::Error::InvalidImage(format!("lzo decode failed: {e:?}")))?;
cap_check(&out, max_out)?;
Ok(out)
}
#[cfg(not(feature = "lzo"))]
fn decompress_lzo(_src: &[u8], _max_out: usize) -> Result<Vec<u8>> {
Err(disabled(Algo::Lzo))
}
#[cfg(feature = "lzo")]
fn compress_lzo(plain: &[u8]) -> Result<Vec<u8>> {
let mut lzo = minilzo_rs::LZO::init()
.map_err(|e| crate::Error::Io(std::io::Error::other(format!("lzo init: {e:?}"))))?;
lzo.compress(plain)
.map_err(|e| crate::Error::Io(std::io::Error::other(format!("lzo encode failed: {e:?}"))))
}
#[cfg(not(feature = "lzo"))]
fn compress_lzo(_plain: &[u8]) -> Result<Vec<u8>> {
Err(disabled(Algo::Lzo))
}
#[cfg(feature = "lzo")]
fn make_reader_lzo<'a, R: std::io::Read + 'a>(r: R) -> Result<Box<dyn std::io::Read + 'a>> {
Ok(Box::new(stream::LzoFrameReader::new(r)))
}
#[cfg(not(feature = "lzo"))]
fn make_reader_lzo<'a, R: std::io::Read + 'a>(_r: R) -> Result<Box<dyn std::io::Read + 'a>> {
Err(disabled(Algo::Lzo))
}
#[cfg(feature = "lzo")]
fn make_writer_lzo<'a, W: std::io::Write + 'a>(w: W) -> Result<Box<dyn std::io::Write + 'a>> {
Ok(Box::new(stream::LzoFrameWriter::new(w)))
}
#[cfg(not(feature = "lzo"))]
fn make_writer_lzo<'a, W: std::io::Write + 'a>(_w: W) -> Result<Box<dyn std::io::Write + 'a>> {
Err(disabled(Algo::Lzo))
}
mod stream;
pub fn detect_path(path: &std::path::Path) -> Result<Option<Algo>> {
if let Some(algo) = Algo::from_extension(path) {
return Ok(Some(algo));
}
if !path.exists() {
return Ok(None);
}
let mut f = std::fs::File::open(path)?;
let mut head = [0u8; 8];
use std::io::Read;
let n = f.read(&mut head)?;
Ok(detect_magic(&head[..n]))
}
pub fn decompress_to_tempfile(
src: &std::path::Path,
algo: Algo,
) -> Result<tempfile::NamedTempFile> {
use std::io::Write;
let f = std::fs::File::open(src)?;
let mut reader = make_reader(algo, std::io::BufReader::new(f))?;
let tmp = tempfile::NamedTempFile::new()?;
{
let mut out = std::io::BufWriter::new(tmp.as_file());
let mut buf = [0u8; 64 * 1024];
loop {
let n = reader.read(&mut buf)?;
if n == 0 {
break;
}
out.write_all(&buf[..n])?;
}
out.flush()?;
}
Ok(tmp)
}
pub fn compress_file_to_file(
src: &std::path::Path,
dst: &std::path::Path,
algo: Algo,
) -> Result<()> {
use std::io::{Read, Write};
let f_in = std::fs::File::open(src)?;
let mut reader = std::io::BufReader::new(f_in);
let f_out = std::fs::File::create(dst)?;
let mut writer = make_writer(algo, std::io::BufWriter::new(f_out))?;
let mut buf = [0u8; 64 * 1024];
loop {
let n = reader.read(&mut buf)?;
if n == 0 {
break;
}
writer.write_all(&buf[..n])?;
}
writer.flush()?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn detect_magic_recognises_known_prefixes() {
assert_eq!(detect_magic(b"\x1f\x8b\x08").unwrap(), Algo::Gzip);
assert_eq!(detect_magic(b"\xfd7zXZ\x00").unwrap(), Algo::Xz);
assert_eq!(detect_magic(b"\x28\xb5\x2f\xfd_").unwrap(), Algo::Zstd);
assert_eq!(detect_magic(b"\x04\x22\x4d\x18_").unwrap(), Algo::Lz4);
assert_eq!(detect_magic(b"\x5d\x00\x00\x80").unwrap(), Algo::Lzma);
assert!(detect_magic(b"plain").is_none());
}
#[test]
fn from_extension_picks_codec() {
let p = std::path::Path::new("/tmp/out.tar.gz");
assert_eq!(Algo::from_extension(p), Some(Algo::Gzip));
let p = std::path::Path::new("disk.tar.zst");
assert_eq!(Algo::from_extension(p), Some(Algo::Zstd));
let p = std::path::Path::new("plain.tar");
assert_eq!(Algo::from_extension(p), None);
}
#[cfg(feature = "gzip")]
#[test]
fn gzip_block_round_trip() {
let plain: Vec<u8> = (0u8..=255).cycle().take(8192).collect();
let enc = compress(Algo::Gzip, &plain).unwrap();
let dec = decompress(Algo::Gzip, &enc, 1 << 16).unwrap();
assert_eq!(dec, plain);
}
#[cfg(feature = "lz4")]
#[test]
fn lz4_block_round_trip() {
let plain: Vec<u8> = (0u8..=255).cycle().take(8192).collect();
let enc = compress(Algo::Lz4, &plain).unwrap();
let dec = decompress(Algo::Lz4, &enc, 1 << 16).unwrap();
assert_eq!(dec, plain);
}
#[cfg(feature = "zstd")]
#[test]
fn zstd_block_round_trip() {
let plain: Vec<u8> = (0u8..=255).cycle().take(8192).collect();
let enc = compress(Algo::Zstd, &plain).unwrap();
let dec = decompress(Algo::Zstd, &enc, 1 << 16).unwrap();
assert_eq!(dec, plain);
}
#[cfg(feature = "xz")]
#[test]
fn xz_block_round_trip() {
let plain: Vec<u8> = (0u8..=255).cycle().take(8192).collect();
let enc = compress(Algo::Xz, &plain).unwrap();
let dec = decompress(Algo::Xz, &enc, 1 << 16).unwrap();
assert_eq!(dec, plain);
}
#[cfg(feature = "lzma")]
#[test]
fn lzma_block_round_trip() {
let plain: Vec<u8> = (0u8..=255).cycle().take(8192).collect();
let enc = compress(Algo::Lzma, &plain).unwrap();
let dec = decompress(Algo::Lzma, &enc, 1 << 16).unwrap();
assert_eq!(dec, plain);
}
#[cfg(feature = "lzo")]
#[test]
fn lzo_block_round_trip() {
let plain: Vec<u8> = (0u8..=255).cycle().take(8192).collect();
let enc = compress(Algo::Lzo, &plain).unwrap();
let dec = decompress(Algo::Lzo, &enc, plain.len()).unwrap();
assert_eq!(dec, plain);
}
#[cfg(feature = "gzip")]
#[test]
fn gzip_stream_round_trip() {
use std::io::{Read, Write};
let plain: Vec<u8> = (0u8..=255).cycle().take(16 * 1024).collect();
let mut compressed = Vec::new();
{
let mut w = make_writer(Algo::Gzip, &mut compressed).unwrap();
w.write_all(&plain).unwrap();
w.flush().unwrap();
}
let mut r = make_reader(Algo::Gzip, &compressed[..]).unwrap();
let mut decoded = Vec::new();
r.read_to_end(&mut decoded).unwrap();
assert_eq!(decoded, plain);
}
#[cfg(feature = "zstd")]
#[test]
fn zstd_stream_round_trip() {
use std::io::{Read, Write};
let plain: Vec<u8> = (0u8..=255).cycle().take(16 * 1024).collect();
let mut compressed = Vec::new();
{
let mut w = make_writer(Algo::Zstd, &mut compressed).unwrap();
w.write_all(&plain).unwrap();
w.flush().unwrap();
}
let mut r = make_reader(Algo::Zstd, &compressed[..]).unwrap();
let mut decoded = Vec::new();
r.read_to_end(&mut decoded).unwrap();
assert_eq!(decoded, plain);
}
}