Improved limiting of max gzip size (#116)

This commit is contained in:
Kornel 2018-07-11 03:36:43 +01:00 committed by Josh Holmer
parent 67fd229f20
commit d2b1d56e8b
4 changed files with 11 additions and 9 deletions

View file

@ -1,12 +1,13 @@
use atomicmin::AtomicMin;
use error::PngError; use error::PngError;
use miniz_oxide::deflate::core::*; use miniz_oxide::deflate::core::*;
pub fn compress_to_vec_oxipng(input: &[u8], level: u8, window_bits: i32, strategy: i32, max_size: Option<usize>) -> Result<Vec<u8>, PngError> { pub fn compress_to_vec_oxipng(input: &[u8], level: u8, window_bits: i32, strategy: i32, max_size: &AtomicMin) -> Result<Vec<u8>, PngError> {
// The comp flags function sets the zlib flag if the window_bits parameter is > 0. // The comp flags function sets the zlib flag if the window_bits parameter is > 0.
let flags = create_comp_flags_from_zip_params(level.into(), window_bits, strategy); let flags = create_comp_flags_from_zip_params(level.into(), window_bits, strategy);
let mut compressor = CompressorOxide::new(flags); let mut compressor = CompressorOxide::new(flags);
// if max size is known, then expect that much data (but no more than input.len()) // if max size is known, then expect that much data (but no more than input.len())
let mut output = Vec::with_capacity(max_size.unwrap_or(input.len() / 2).min(input.len())); let mut output = Vec::with_capacity(max_size.get().unwrap_or(input.len() / 2).min(input.len()));
// # Unsafe // # Unsafe
// We trust compress to not read the uninitialized bytes. // We trust compress to not read the uninitialized bytes.
unsafe { unsafe {
@ -32,7 +33,7 @@ pub fn compress_to_vec_oxipng(input: &[u8], level: u8, window_bits: i32, strateg
break; break;
} }
TDEFLStatus::Okay => { TDEFLStatus::Okay => {
if let Some(max) = max_size { if let Some(max) = max_size.get() {
if output.len() > max { if output.len() > max {
return Err(PngError::DeflatedDataTooLong(output.len())) return Err(PngError::DeflatedDataTooLong(output.len()))
} }

View file

@ -1,3 +1,4 @@
use atomicmin::AtomicMin;
use error::PngError; use error::PngError;
use miniz_oxide; use miniz_oxide;
use std::cmp::max; use std::cmp::max;
@ -13,7 +14,7 @@ pub fn inflate(data: &[u8]) -> Result<Vec<u8>, PngError> {
} }
/// Compress a data stream using the DEFLATE algorithm /// Compress a data stream using the DEFLATE algorithm
pub fn deflate(data: &[u8], zc: u8, zs: u8, zw: u8, max_size: Option<usize>) -> Result<Vec<u8>, PngError> { pub fn deflate(data: &[u8], zc: u8, zs: u8, zw: u8, max_size: &AtomicMin) -> Result<Vec<u8>, PngError> {
#[cfg(feature = "cfzlib")] #[cfg(feature = "cfzlib")]
{ {
if is_cfzlib_supported() { if is_cfzlib_supported() {
@ -40,7 +41,7 @@ fn is_cfzlib_supported() -> bool {
} }
#[cfg(feature = "cfzlib")] #[cfg(feature = "cfzlib")]
pub fn cfzlib_deflate(data: &[u8], level: u8, strategy: u8, window_bits: u8, max_size: Option<usize>) -> Result<Vec<u8>, PngError> { pub fn cfzlib_deflate(data: &[u8], level: u8, strategy: u8, window_bits: u8, max_size: &AtomicMin) -> Result<Vec<u8>, PngError> {
use std::mem; use std::mem;
use cloudflare_zlib_sys::*; use cloudflare_zlib_sys::*;
@ -58,7 +59,7 @@ pub fn cfzlib_deflate(data: &[u8], level: u8, strategy: u8, window_bits: u8, max
} }
let upper_bound = deflateBound(&mut stream, data.len() as uLong) as usize; let upper_bound = deflateBound(&mut stream, data.len() as uLong) as usize;
let max_size = max_size.unwrap_or(upper_bound).min(upper_bound); let max_size = max_size.get().unwrap_or(upper_bound).min(upper_bound);
// it's important to have the capacity pre-allocated, // it's important to have the capacity pre-allocated,
// as unsafe set_len is called later // as unsafe set_len is called later
let mut out = Vec::with_capacity(max_size); let mut out = Vec::with_capacity(max_size);
@ -70,7 +71,7 @@ pub fn cfzlib_deflate(data: &[u8], level: u8, strategy: u8, window_bits: u8, max
stream.avail_out = out.capacity() as uInt; stream.avail_out = out.capacity() as uInt;
match deflate(&mut stream, Z_FINISH) { match deflate(&mut stream, Z_FINISH) {
Z_STREAM_END => {}, Z_STREAM_END => {},
Z_OK => return Err(PngError::DeflatedDataTooLong(max_size)), Z_OK => return Err(PngError::DeflatedDataTooLong(stream.total_out as usize)),
_ => return Err(PngError::new("deflate")), _ => return Err(PngError::new("deflate")),
} }
if Z_OK != deflateEnd(&mut stream) { if Z_OK != deflateEnd(&mut stream) {

View file

@ -506,7 +506,7 @@ fn optimize_png(
.filter_map(|trial| { .filter_map(|trial| {
let filtered = &filters[&trial.filter]; let filtered = &filters[&trial.filter];
let new_idat = if opts.deflate == Deflaters::Zlib { let new_idat = if opts.deflate == Deflaters::Zlib {
deflate::deflate(filtered, trial.compression, trial.strategy, opts.window, best_size.get()) deflate::deflate(filtered, trial.compression, trial.strategy, opts.window, &best_size)
} else { } else {
deflate::zopfli_deflate(filtered) deflate::zopfli_deflate(filtered)
}; };

View file

@ -625,7 +625,7 @@ impl PngData {
STD_COMPRESSION, STD_COMPRESSION,
STD_STRATEGY, STD_STRATEGY,
STD_WINDOW, STD_WINDOW,
best_size.get(), &best_size,
).ok() ).ok()
.as_ref().map(|l| { .as_ref().map(|l| {
best_size.set_min(l.len()); best_size.set_min(l.len());