//! Extra streaming compression functionality. //! //! As of now this is mainly inteded for use to build a higher-level wrapper. //! //! There is no DeflateState as the needed state is contained in the compressor struct itself. use std::convert::{AsMut, AsRef}; use crate::deflate::core::{compress, CompressorOxide, TDEFLFlush, TDEFLStatus}; use crate::{MZError, MZFlush, MZStatus, StreamResult}; /// Try to compress from input to output with the given Compressor /// /// # Errors /// /// Returns `MZError::Buf` If the size of the `output` slice is empty or no progress was made due to /// lack of expected input data or called after the compression was finished without /// MZFlush::Finish. /// /// Returns `MZError::Param` if the compressor parameters are set wrong. pub fn deflate( compressor: &mut CompressorOxide, input: &[u8], output: &mut [u8], flush: MZFlush, ) -> StreamResult { if output.is_empty() { return StreamResult::error(MZError::Buf); } if compressor.prev_return_status() == TDEFLStatus::Done { return if flush == MZFlush::Finish { StreamResult { bytes_written: 0, bytes_consumed: 0, status: Ok(MZStatus::StreamEnd), } } else { StreamResult::error(MZError::Buf) }; } let mut bytes_written = 0; let mut bytes_consumed = 0; let mut next_in = input.as_ref(); let mut next_out = output.as_mut(); let status = loop { let in_bytes; let out_bytes; let defl_status = { let res = compress(compressor, next_in, next_out, TDEFLFlush::from(flush)); in_bytes = res.1; out_bytes = res.2; res.0 }; next_in = &next_in[in_bytes..]; next_out = &mut next_out[out_bytes..]; bytes_consumed += in_bytes; bytes_written += out_bytes; // Check if we are done, or compression failed. match defl_status { TDEFLStatus::BadParam => break Err(MZError::Param), // Don't think this can happen as we're not using a custom callback. TDEFLStatus::PutBufFailed => break Err(MZError::Stream), TDEFLStatus::Done => break Ok(MZStatus::StreamEnd), _ => (), }; // All the output space was used, so wait for more. if next_out.is_empty() { break Ok(MZStatus::Ok); } if next_in.is_empty() && (flush != MZFlush::Finish) { let total_changed = bytes_written > 0 || bytes_consumed > 0; break if (flush != MZFlush::None) || total_changed { // We wrote or consumed something, and/or did a flush (sync/partial etc.). Ok(MZStatus::Ok) } else { // No more input data, not flushing, and nothing was consumed or written, // so couldn't make any progress. Err(MZError::Buf) }; } }; StreamResult { bytes_consumed, bytes_written, status, } } #[cfg(test)] mod test { use super::deflate; use crate::deflate::CompressorOxide; use crate::inflate::decompress_to_vec_zlib; use crate::{MZFlush, MZStatus}; #[test] fn test_state() { let data = b"Hello zlib!"; let mut compressed = vec![0; 50]; let mut compressor = Box::::default(); let res = deflate(&mut compressor, data, &mut compressed, MZFlush::Finish); let status = res.status.expect("Failed to compress!"); let decomp = decompress_to_vec_zlib(&compressed).expect("Failed to decompress compressed data"); assert_eq!(status, MZStatus::StreamEnd); assert_eq!(decomp[..], data[..]); assert_eq!(res.bytes_consumed, data.len()); } }