Skip to content

Commit

Permalink
Code size reduction from panic reduction (#145)
Browse files Browse the repository at this point in the history
* Move misplaced doccomment

* Avoid slice_start_index_len_fail

* Make init_tree fallible

* Add bounds checks to init_tree

This eliminates costlier implicit panicking bounds checks

* Reduce panics in compress_to_vec_inner
  • Loading branch information
kornelski committed Jan 28, 2024
1 parent 6e3e813 commit 201ef39
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 34 deletions.
6 changes: 2 additions & 4 deletions miniz_oxide/src/deflate/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,8 @@ const fn read_u16_le(slice: &[u8], pos: usize) -> u16 {
pub struct CompressorOxide {
lz: LZOxide,
params: ParamsOxide,
/// Put HuffmanOxide on the heap with default trick to avoid
/// excessive stack copies.
huff: Box<HuffmanOxide>,
dict: DictOxide,
}
Expand All @@ -427,8 +429,6 @@ impl CompressorOxide {
CompressorOxide {
lz: LZOxide::new(),
params: ParamsOxide::new(flags),
/// Put HuffmanOxide on the heap with default trick to avoid
/// excessive stack copies.
huff: Box::default(),
dict: DictOxide::new(flags),
}
Expand Down Expand Up @@ -521,8 +521,6 @@ impl Default for CompressorOxide {
CompressorOxide {
lz: LZOxide::new(),
params: ParamsOxide::new(DEFAULT_FLAGS),
/// Put HuffmanOxide on the heap with default trick to avoid
/// excessive stack copies.
huff: Box::default(),
dict: DictOxide::new(DEFAULT_FLAGS),
}
Expand Down
11 changes: 5 additions & 6 deletions miniz_oxide/src/deflate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,31 +118,30 @@ pub fn compress_to_vec_zlib(input: &[u8], level: u8) -> Vec<u8> {
}

/// Simple function to compress data to a vec.
fn compress_to_vec_inner(input: &[u8], level: u8, window_bits: i32, strategy: i32) -> Vec<u8> {
fn compress_to_vec_inner(mut input: &[u8], level: u8, window_bits: i32, strategy: i32) -> Vec<u8> {
// 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 mut compressor = CompressorOxide::new(flags);
let mut output = vec![0; ::core::cmp::max(input.len() / 2, 2)];

let mut in_pos = 0;
let mut out_pos = 0;
loop {
let (status, bytes_in, bytes_out) = compress(
&mut compressor,
&input[in_pos..],
input,
&mut output[out_pos..],
TDEFLFlush::Finish,
);

out_pos += bytes_out;
in_pos += bytes_in;

match status {
TDEFLStatus::Done => {
output.truncate(out_pos);
break;
}
TDEFLStatus::Okay => {
TDEFLStatus::Okay if bytes_in <= input.len() => {
input = &input[bytes_in..];

// We need more space, so resize the vector.
if output.len().saturating_sub(out_pos) < 30 {
output.resize(output.len() * 2, 0)
Expand Down
59 changes: 39 additions & 20 deletions miniz_oxide/src/inflate/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -684,37 +684,48 @@ static REVERSED_BITS_LOOKUP: [u32; 1024] = {
table
};

fn init_tree(r: &mut DecompressorOxide, l: &mut LocalVars) -> Action {
fn init_tree(r: &mut DecompressorOxide, l: &mut LocalVars) -> Option<Action> {
loop {
let table = &mut r.tables[r.block_type as usize];
let table_size = r.table_sizes[r.block_type as usize] as usize;
let bt = r.block_type as usize;
if bt >= r.tables.len() {
return None;
}
let table = &mut r.tables[bt];
let table_size = r.table_sizes[bt] as usize;
if table_size > table.code_size.len() {
return None;
}
let mut total_symbols = [0u32; 16];
let mut next_code = [0u32; 17];
memset(&mut table.look_up[..], 0);
memset(&mut table.tree[..], 0);

for &code_size in &table.code_size[..table_size] {
total_symbols[code_size as usize] += 1;
let cs = code_size as usize;
if cs >= total_symbols.len() {
return None;
}
total_symbols[cs] += 1;
}

let mut used_symbols = 0;
let mut total = 0;
for i in 1..16 {
used_symbols += total_symbols[i];
total += total_symbols[i];
for (ts, next) in total_symbols.iter().copied().zip(next_code.iter_mut().skip(1)).skip(1) {
used_symbols += ts;
total += ts;
total <<= 1;
next_code[i + 1] = total;
*next = total;
}

if total != 65_536 && used_symbols > 1 {
return Action::Jump(BadTotalSymbols);
return Some(Action::Jump(BadTotalSymbols));
}

let mut tree_next = -1;
for symbol_index in 0..table_size {
let mut rev_code = 0;
let code_size = table.code_size[symbol_index];
if code_size == 0 {
if code_size == 0 || usize::from(code_size) >= next_code.len() {
continue;
}

Expand Down Expand Up @@ -753,23 +764,31 @@ fn init_tree(r: &mut DecompressorOxide, l: &mut LocalVars) -> Action {
for _ in FAST_LOOKUP_BITS + 1..code_size {
rev_code >>= 1;
tree_cur -= (rev_code & 1) as i16;
if table.tree[(-tree_cur - 1) as usize] == 0 {
table.tree[(-tree_cur - 1) as usize] = tree_next as i16;
let tree_index = (-tree_cur - 1) as usize;
if tree_index >= table.tree.len() {
return None;
}
if table.tree[tree_index] == 0 {
table.tree[tree_index] = tree_next as i16;
tree_cur = tree_next;
tree_next -= 2;
} else {
tree_cur = table.tree[(-tree_cur - 1) as usize];
tree_cur = table.tree[tree_index];
}
}

rev_code >>= 1;
tree_cur -= (rev_code & 1) as i16;
table.tree[(-tree_cur - 1) as usize] = symbol_index as i16;
let tree_index = (-tree_cur - 1) as usize;
if tree_index >= table.tree.len() {
return None;
}
table.tree[tree_index] = symbol_index as i16;
}

if r.block_type == 2 {
l.counter = 0;
return Action::Jump(ReadLitlenDistTablesCodeSize);
return Some(Action::Jump(ReadLitlenDistTablesCodeSize));
}

if r.block_type == 0 {
Expand All @@ -779,7 +798,7 @@ fn init_tree(r: &mut DecompressorOxide, l: &mut LocalVars) -> Action {
}

l.counter = 0;
Action::Jump(DecodeLitlen)
Some(Action::Jump(DecodeLitlen))
}

// A helper macro for generating the state machine.
Expand Down Expand Up @@ -1192,7 +1211,7 @@ pub fn decompress(
0 => Action::Jump(BlockTypeNoCompression),
1 => {
start_static_table(r);
init_tree(r, l)
init_tree(r, l).unwrap_or(Action::End(TINFLStatus::Failed))
},
2 => {
l.counter = 0;
Expand Down Expand Up @@ -1357,7 +1376,7 @@ pub fn decompress(
})
} else {
r.table_sizes[HUFFLEN_TABLE] = 19;
init_tree(r, &mut l)
init_tree(r, &mut l).unwrap_or(Action::End(TINFLStatus::Failed))
}
}),

Expand Down Expand Up @@ -1392,7 +1411,7 @@ pub fn decompress(
.copy_from_slice(&r.len_codes[dist_table_start..dist_table_end]);

r.block_type -= 1;
init_tree(r, &mut l)
init_tree(r, &mut l).unwrap_or(Action::End(TINFLStatus::Failed))
}
}),

Expand Down Expand Up @@ -1850,7 +1869,7 @@ mod test {
counter: d.counter,
num_extra: d.num_extra,
};
init_tree(&mut d, &mut l);
init_tree(&mut d, &mut l).unwrap();
let llt = &d.tables[LITLEN_TABLE];
let dt = &d.tables[DIST_TABLE];
assert_eq!(masked_lookup(llt, 0b00001100), (0, 8));
Expand Down
13 changes: 9 additions & 4 deletions miniz_oxide/src/inflate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ pub fn decompress_to_vec_zlib_with_limit(
/// Returns [`Vec`] of decompressed data on success and the [error struct][DecompressError] with details on failure.
#[cfg(feature = "with-alloc")]
fn decompress_to_vec_inner(
input: &[u8],
mut input: &[u8],
flags: u32,
max_output_size: usize,
) -> Result<Vec<u8>, DecompressError> {
Expand All @@ -191,14 +191,12 @@ fn decompress_to_vec_inner(

let mut decomp = Box::<DecompressorOxide>::default();

let mut in_pos = 0;
let mut out_pos = 0;
loop {
// Wrap the whole output slice so we know we have enough of the
// decompressed data for matches.
let (status, in_consumed, out_consumed) =
decompress(&mut decomp, &input[in_pos..], &mut ret, out_pos, flags);
in_pos += in_consumed;
decompress(&mut decomp, input, &mut ret, out_pos, flags);
out_pos += out_consumed;

match status {
Expand All @@ -208,6 +206,13 @@ fn decompress_to_vec_inner(
}

TINFLStatus::HasMoreOutput => {
// in_consumed is not expected to be out of bounds,
// but the check eliminates a panicking code path
if in_consumed > input.len() {
return decompress_error(TINFLStatus::HasMoreOutput, ret);
}
input = &input[in_consumed..];

// if the buffer has already reached the size limit, return an error
if ret.len() >= max_output_size {
return decompress_error(TINFLStatus::HasMoreOutput, ret);
Expand Down

0 comments on commit 201ef39

Please sign in to comment.