oxipng/src/deflate/deflater.rs
Ingvar Stepanyan d3c15d13c9 Add libdeflater as an option
libdeflater is a Rust wrapper around
[libdeflate](https://github.com/ebiggers/libdeflate) - an alternative
heavily optimised library for deflate/zlib/gzip compression and
decompression that is intended for situations where upper bounds of the
output are well-known.

In my benchmarks on test files in the repo it has shown to be usually
both slightly faster and providing better compressed output than
cloudflare-zlib, but in some cases showing the opposite, so rather
than swapping defaults, it's currently provided as another option,
similarly to zopfli.

Since it's not strictly better in all cases, I'm not providing median
numbers, but you can check distribution histograms for time and size
differences here (all using `oxipng -o 6 -t 6 -P`):
https://docs.google.com/spreadsheets/d/1WOKgeYZBhLkQvMGAC36snN4azilElzOFhx63RJu0EZY/edit?usp=sharing
2020-03-16 14:18:20 +00:00

31 lines
1.2 KiB
Rust

use crate::{PngError, PngResult};
use crate::atomicmin::AtomicMin;
use libdeflater::{CompressionError, CompressionLvl, Compressor};
pub fn deflate(data: &[u8], max_size: &AtomicMin) -> PngResult<Vec<u8>> {
let mut compressor = Compressor::new(CompressionLvl::best());
let capacity = max_size.get().unwrap_or(data.len() / 2);
let mut dest = Vec::with_capacity(capacity);
unsafe {
// This is ok because the Vec contains Copy-able data (u8)
// and because libdeflater wrapper doesn't try to read
// the bytes from the target.
//
// That said, it should be able to accept MaybeUninit instead,
// so I raised an upstream issue that should make this safer:
// https://github.com/adamkewley/libdeflater/issues/1
dest.set_len(capacity);
}
let len = compressor
.zlib_compress(data, &mut dest)
.map_err(|err| match err {
CompressionError::InsufficientSpace => PngError::DeflatedDataTooLong(capacity),
})?;
if let Some(max) = max_size.get() {
if len > max {
return Err(PngError::DeflatedDataTooLong(max));
}
}
dest.truncate(len);
Ok(dest)
}