use ::bzip2::read::BzDecoder;
use crate::error::{Error, Result};
const MAGIC: &[u8] = b"BZh";
pub fn decode(data: &[u8], raw_size: usize, path: &str, offset: u64) -> Result<Vec<u8>> {
if !data.starts_with(MAGIC) {
return Err(Error::corrupt(
path,
offset,
"a bzip2 block that does not begin with the bzip2 signature",
));
}
let mut out = Vec::with_capacity(raw_size.min(1 << 20));
super::inflate_bounded(
BzDecoder::new(data),
&mut out,
raw_size,
"bzip2",
path,
offset,
)?;
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write as _;
fn compress(data: &[u8]) -> Vec<u8> {
let mut encoder =
::bzip2::write::BzEncoder::new(Vec::new(), ::bzip2::Compression::default());
encoder.write_all(data).expect("compress");
encoder.finish().expect("finish")
}
#[test]
fn a_bzip2_stream_round_trips() {
let data = b"ACGTACGTNNNN".repeat(500);
let compressed = compress(&data);
assert!(compressed.starts_with(MAGIC));
assert_eq!(
decode(&compressed, data.len(), "test", 0).expect("decode"),
data
);
}
#[test]
fn an_empty_stream_round_trips() {
let compressed = compress(b"");
assert_eq!(decode(&compressed, 0, "test", 0).expect("decode"), b"");
}
#[test]
fn a_stream_without_the_signature_is_refused() {
let error = decode(b"not bzip2 at all", 16, "test", 0).expect_err("no signature");
assert!(error.to_string().contains("bzip2 signature"), "{error}");
}
#[test]
fn a_truncated_stream_is_refused_rather_than_returning_what_it_had() {
let compressed = compress(&b"ACGT".repeat(1000));
let error = decode(&compressed[..compressed.len() / 2], 4000, "test", 0);
assert!(error.is_err(), "half a stream decoded");
}
#[test]
fn every_prefix_fails_without_panicking() {
let compressed = compress(&b"ACGTNNNN".repeat(100));
for cut in 0..compressed.len() {
let _ = decode(&compressed[..cut], 800, "test", 0);
}
}
}