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
31 lines
1.2 KiB
Rust
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)
|
|
}
|