blob: f17bdbfb5ef5e310cf599ac42b6c2e90cae1c144 [file] [log] [blame]
use crate::{codec::Encode, util::PartialBuffer};
use std::{fmt, io};
use bzip2::{Action, Compress, Compression, Status};
pub struct BzEncoder {
compress: Compress,
}
impl fmt::Debug for BzEncoder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"BzEncoder {{total_in: {}, total_out: {}}}",
self.compress.total_in(),
self.compress.total_out()
)
}
}
impl BzEncoder {
/// Creates a new stream prepared for compression.
///
/// The `work_factor` parameter controls how the compression phase behaves
/// when presented with worst case, highly repetitive, input data. If
/// compression runs into difficulties caused by repetitive data, the
/// library switches from the standard sorting algorithm to a fallback
/// algorithm. The fallback is slower than the standard algorithm by perhaps
/// a factor of three, but always behaves reasonably, no matter how bad the
/// input.
///
/// Lower values of `work_factor` reduce the amount of effort the standard
/// algorithm will expend before resorting to the fallback. You should set
/// this parameter carefully; too low, and many inputs will be handled by
/// the fallback algorithm and so compress rather slowly, too high, and your
/// average-to-worst case compression times can become very large. The
/// default value of 30 gives reasonable behaviour over a wide range of
/// circumstances.
///
/// Allowable values range from 0 to 250 inclusive. 0 is a special case,
/// equivalent to using the default value of 30.
pub(crate) fn new(level: Compression, work_factor: u32) -> Self {
Self {
compress: Compress::new(level, work_factor),
}
}
fn encode(
&mut self,
input: &mut PartialBuffer<impl AsRef<[u8]>>,
output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
action: Action,
) -> io::Result<Status> {
let prior_in = self.compress.total_in();
let prior_out = self.compress.total_out();
let status = self
.compress
.compress(input.unwritten(), output.unwritten_mut(), action)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
input.advance((self.compress.total_in() - prior_in) as usize);
output.advance((self.compress.total_out() - prior_out) as usize);
Ok(status)
}
}
impl Encode for BzEncoder {
fn encode(
&mut self,
input: &mut PartialBuffer<impl AsRef<[u8]>>,
output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
) -> io::Result<()> {
match self.encode(input, output, Action::Run)? {
// Decompression went fine, nothing much to report.
Status::Ok => Ok(()),
// The Flush action on a compression went ok.
Status::FlushOk => unreachable!(),
// The Run action on compression went ok.
Status::RunOk => Ok(()),
// The Finish action on compression went ok.
Status::FinishOk => unreachable!(),
// The stream's end has been met, meaning that no more data can be input.
Status::StreamEnd => unreachable!(),
// There was insufficient memory in the input or output buffer to complete
// the request, but otherwise everything went normally.
Status::MemNeeded => Err(io::Error::new(io::ErrorKind::Other, "out of memory")),
}
}
fn flush(
&mut self,
output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
) -> io::Result<bool> {
match self.encode(&mut PartialBuffer::new(&[][..]), output, Action::Flush)? {
// Decompression went fine, nothing much to report.
Status::Ok => unreachable!(),
// The Flush action on a compression went ok.
Status::FlushOk => Ok(false),
// The Run action on compression went ok.
Status::RunOk => Ok(true),
// The Finish action on compression went ok.
Status::FinishOk => unreachable!(),
// The stream's end has been met, meaning that no more data can be input.
Status::StreamEnd => unreachable!(),
// There was insufficient memory in the input or output buffer to complete
// the request, but otherwise everything went normally.
Status::MemNeeded => Err(io::Error::new(io::ErrorKind::Other, "out of memory")),
}
}
fn finish(
&mut self,
output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
) -> io::Result<bool> {
match self.encode(&mut PartialBuffer::new(&[][..]), output, Action::Finish)? {
// Decompression went fine, nothing much to report.
Status::Ok => Ok(false),
// The Flush action on a compression went ok.
Status::FlushOk => unreachable!(),
// The Run action on compression went ok.
Status::RunOk => unreachable!(),
// The Finish action on compression went ok.
Status::FinishOk => Ok(false),
// The stream's end has been met, meaning that no more data can be input.
Status::StreamEnd => Ok(true),
// There was insufficient memory in the input or output buffer to complete
// the request, but otherwise everything went normally.
Status::MemNeeded => Err(io::Error::new(io::ErrorKind::Other, "out of memory")),
}
}
}