use crate::{
constants,
errors::UZ2LibErrors,
helper::get_sha1_hasher,
types::{InputArguments, ProcessingResult},
};
use flate2::write::ZlibDecoder;
use sha1_smol::Sha1;
use std::{
fs::File,
io::{BufReader, BufWriter, Error, ErrorKind, Read, Write},
time::Instant,
};
pub fn decompress(
input_stream: &mut BufReader<File>,
output_stream: &mut BufWriter<File>,
input_arguments: &InputArguments,
) -> Result<ProcessingResult, UZ2LibErrors> {
let mut chunk_count: u32 = 0;
let mut buffer: Vec<u8> = vec![0u8; constants::COMPRESSED_CHUNK_SIZE];
let mut compressed_chunk_size_b: [u8; 4] = [0u8; 4];
let mut uncompressed_chunk_size_b: [u8; 4] = [0u8; 4];
let mut decoder: ZlibDecoder<Vec<u8>> = ZlibDecoder::new(Vec::new());
let mut hasher: Option<Sha1> = get_sha1_hasher(&input_arguments.log_level);
let start: Instant = Instant::now();
loop {
match input_stream.read_exact(&mut compressed_chunk_size_b) {
Ok(()) => {}
Err(e) => {
if e.kind() == std::io::ErrorKind::UnexpectedEof {
break;
}
return Err(UZ2LibErrors::IOError(Error::new(
e.kind(),
"Failed to read compressed chunk size from input",
)));
}
}
match input_stream.read_exact(&mut uncompressed_chunk_size_b) {
Ok(()) => {}
Err(e) => {
if e.kind() == std::io::ErrorKind::UnexpectedEof {
return Err(UZ2LibErrors::IOError(Error::new(
e.kind(),
"Tried to read beyond end of file!",
)));
}
return Err(UZ2LibErrors::IOError(Error::new(
e.kind(),
"Failed to read uncompressed chunk size from input",
)));
}
}
let compressed_chunk_size: usize = u32::from_le_bytes(compressed_chunk_size_b) as usize;
if compressed_chunk_size > constants::COMPRESSED_CHUNK_SIZE {
return Err(UZ2LibErrors::IOError(Error::new(
ErrorKind::InvalidData,
format!(
"compressed_chunk_size ({}) is bigger than max allowed chunk size ({})",
compressed_chunk_size,
constants::COMPRESSED_CHUNK_SIZE
),
)));
}
let uncompressed_chunk_size: usize = u32::from_le_bytes(uncompressed_chunk_size_b) as usize;
if uncompressed_chunk_size > constants::UNCOMPRESSED_CHUNK_SIZE {
return Err(UZ2LibErrors::IOError(Error::new(
ErrorKind::InvalidData,
format!(
"uncompressed_chunk_size ({}) is bigger than max allowed chunk size ({})",
uncompressed_chunk_size,
constants::UNCOMPRESSED_CHUNK_SIZE
),
)));
}
match input_stream.read_exact(&mut buffer[..compressed_chunk_size]) {
Ok(()) => {}
Err(e) => {
if e.kind() == std::io::ErrorKind::UnexpectedEof {
break;
}
return Err(UZ2LibErrors::IOError(Error::new(
e.kind(),
"Failed to read chunk from input",
)));
}
}
let decompressed_bytes: Vec<u8> =
decompress_single_chunk(&buffer[..compressed_chunk_size], &mut decoder)?;
if decompressed_bytes.len() != uncompressed_chunk_size {
return Err(UZ2LibErrors::IOError(Error::new(
ErrorKind::InvalidData,
format!(
"The decompressed chunk has a different size ({}) than the saved value ({}). Damaged file?",
decompressed_bytes.len(),
uncompressed_chunk_size
),
)));
}
output_stream.write_all(&decompressed_bytes)?;
if let Some(ref mut sha1) = hasher {
sha1.update(&decompressed_bytes);
}
chunk_count += 1;
}
let input_size: u64 = input_stream.get_mut().metadata()?.len();
let output_size: u64 = output_stream.get_mut().metadata()?.len();
Ok(ProcessingResult {
time: start.elapsed(),
chunk_count,
hasher,
input_file_size: input_size,
output_file_size: output_size,
})
}
fn decompress_single_chunk(
buffer: &[u8],
decoder: &mut ZlibDecoder<Vec<u8>>,
) -> Result<Vec<u8>, UZ2LibErrors> {
decoder.write_all(buffer)?;
let decompressed_chunk: Vec<u8> = decoder.reset(Vec::new())?;
Ok(decompressed_chunk)
}