use crate::{ deflate::{ flush_pending, read_buf_window, zng_tr_stored_block, BlockState, DeflateStream, MAX_STORED, }, DeflateFlush, }; pub fn deflate_stored(stream: &mut DeflateStream, flush: DeflateFlush) -> BlockState { // Smallest worthy block size when not flushing or finishing. By default // this is 32K. This can be as small as 507 bytes for memLevel == 1. For // large input and output buffers, the stored block size will be larger. let min_block = Ord::min( stream.state.bit_writer.pending.capacity() - 5, stream.state.w_size, ); // Copy as many min_block or larger stored blocks directly to next_out as // possible. If flushing, copy the remaining available input to next_out as // stored blocks, if there is enough space. // unsigned len, left, have, last = 0; let mut have; let mut last = false; let mut used = stream.avail_in; loop { // maximum deflate stored block length let mut len = MAX_STORED; // number of header bytes have = ((stream.state.bit_writer.bits_used + 42) / 8) as usize; // we need room for at least the header if stream.avail_out < have as u32 { break; } let left = stream.state.strstart as isize - stream.state.block_start; let left = Ord::max(0, left) as usize; have = stream.avail_out as usize - have; if len > left + stream.avail_in as usize { // limit len to the input len = left + stream.avail_in as usize; } len = Ord::min(len, have); // If the stored block would be less than min_block in length, or if // unable to copy all of the available input when flushing, then try // copying to the window and the pending buffer instead. Also don't // write an empty block when flushing -- deflate() does that. if len < min_block && ((len == 0 && flush != DeflateFlush::Finish) || flush == DeflateFlush::NoFlush || len != left + stream.avail_in as usize) { break; } // Make a dummy stored block in pending to get the header bytes, // including any pending bits. This also updates the debugging counts. last = flush == DeflateFlush::Finish && len == left + stream.avail_in as usize; zng_tr_stored_block(stream.state, 0..0, last); /* Replace the lengths in the dummy stored block with len. */ stream.state.bit_writer.pending.rewind(4); stream .state .bit_writer .pending .extend(&(len as u16).to_le_bytes()); stream .state .bit_writer .pending .extend(&(!len as u16).to_le_bytes()); // Write the stored block header bytes. flush_pending(stream); // Update debugging counts for the data about to be copied. stream.state.bit_writer.cmpr_bits_add(len << 3); stream.state.bit_writer.sent_bits_add(len << 3); if left > 0 { // SAFETY: `len` is effectively `min(stream.avail_in, stream.avail_out)`, so any reads // of `len` won't go out of bounds on `next_out`. `left` is calculated from indices of // the window, so `left` reads of the window won't go out of bounds. let left = Ord::min(left, len); let src = &stream.state.window.filled()[stream.state.block_start as usize..]; unsafe { core::ptr::copy_nonoverlapping(src.as_ptr(), stream.next_out, left) }; stream.next_out = stream.next_out.wrapping_add(left); stream.avail_out = stream.avail_out.wrapping_sub(left as _); stream.total_out = stream.total_out.wrapping_add(left as _); stream.state.block_start += left as isize; len -= left; } // Copy uncompressed bytes directly from next_in to next_out, updating the check value. if len > 0 { read_buf_direct_copy(stream, len); } if last { break; } } // Update the sliding window with the last s->w_size bytes of the copied // data, or append all of the copied data to the existing window if less // than s->w_size bytes were copied. Also update the number of bytes to // insert in the hash tables, in the event that deflateParams() switches to // a non-zero compression level. used -= stream.avail_in; /* number of input bytes directly copied */ if used > 0 { let state = &mut stream.state; // If any input was used, then no unused input remains in the window, therefore s->block_start == s->strstart. if used as usize >= state.w_size { /* supplant the previous history */ state.matches = 2; /* clear hash */ // SAFETY: we've advanced the next_in pointer at minimum w_size bytes // read_buf_direct_copy(), so we are able to backtrack that number of bytes. let src = stream.next_in.wrapping_sub(state.w_size); unsafe { state.window.copy_and_initialize(0..state.w_size, src) }; state.strstart = state.w_size; state.insert = state.strstart; } else { if state.window_size - state.strstart <= used as usize { /* Slide the window down. */ state.strstart -= state.w_size; // make sure we don't copy uninitialized bytes. While we discard the first lower w_size // bytes, it is not guaranteed that the upper w_size bytes are all initialized let copy = Ord::min(state.strstart, state.window.filled().len() - state.w_size); state .window .filled_mut() .copy_within(state.w_size..state.w_size + copy, 0); if state.matches < 2 { state.matches += 1; /* add a pending slide_hash() */ } state.insert = Ord::min(state.insert, state.strstart); } // SAFETY: we've advanced the next_in pointer at least `used` bytes // read_buf_direct_copy(), so we are able to backtrack that number of bytes. let src = stream.next_in.wrapping_sub(used as usize); let dst = state.strstart..state.strstart + used as usize; unsafe { state.window.copy_and_initialize(dst, src) }; state.strstart += used as usize; state.insert += Ord::min(used as usize, state.w_size - state.insert); } state.block_start = state.strstart as isize; } if last { return BlockState::FinishDone; } // If flushing and all input has been consumed, then done. if flush != DeflateFlush::NoFlush && flush != DeflateFlush::Finish && stream.avail_in == 0 && stream.state.strstart as isize == stream.state.block_start { return BlockState::BlockDone; } // Fill the window with any remaining input let mut have = stream.state.window_size - stream.state.strstart; if stream.avail_in as usize > have && stream.state.block_start >= stream.state.w_size as isize { // slide the window down let state = &mut stream.state; state.block_start -= state.w_size as isize; state.strstart -= state.w_size; // make sure we don't copy uninitialized bytes. While we discard the first lower w_size // bytes, it is not guaranteed that the upper w_size bytes are all initialized let copy = Ord::min(state.strstart, state.window.filled().len() - state.w_size); state .window .filled_mut() .copy_within(state.w_size..state.w_size + copy, 0); if state.matches < 2 { // add a pending slide_hash state.matches += 1; } have += state.w_size; // more space now state.insert = Ord::min(state.insert, state.strstart); } let have = Ord::min(have, stream.avail_in as usize); if have > 0 { read_buf_window(stream, stream.state.strstart, have); let state = &mut stream.state; state.strstart += have; state.insert += Ord::min(have, state.w_size - state.insert); } // There was not enough avail_out to write a complete worthy or flushed // stored block to next_out. Write a stored block to pending instead, if we // have enough input for a worthy block, or if flushing and there is enough // room for the remaining input as a stored block in the pending buffer. // number of header bytes let state = &mut stream.state; let have = ((state.bit_writer.bits_used + 42) >> 3) as usize; // maximum stored block length that will fit in pending: let have = Ord::min(state.bit_writer.pending.capacity() - have, MAX_STORED); let min_block = Ord::min(have, state.w_size); let left = state.strstart as isize - state.block_start; if left >= min_block as isize || ((left > 0 || flush == DeflateFlush::Finish) && flush != DeflateFlush::NoFlush && stream.avail_in == 0 && left <= have as isize) { let len = Ord::min(left as usize, have); // TODO wrapping? last = flush == DeflateFlush::Finish && stream.avail_in == 0 && len == (left as usize); let range = state.block_start as usize..state.block_start as usize + len; zng_tr_stored_block(state, range, last); state.block_start += len as isize; flush_pending(stream); } // We've done all we can with the available input and output. if last { BlockState::FinishStarted } else { BlockState::NeedMore } } fn read_buf_direct_copy(stream: &mut DeflateStream, size: usize) -> usize { let len = Ord::min(stream.avail_in as usize, size); let output = stream.next_out; if len == 0 { return 0; } stream.avail_in -= len as u32; // SAFETY: len is effectively bounded by next_in and next_out (via size derived in the calling // function), so copies are in-bounds. if stream.state.wrap == 2 { // we likely cannot fuse the crc32 and the copy here because the input can be changed by // a concurrent thread. Therefore it cannot be converted into a slice! unsafe { core::ptr::copy_nonoverlapping(stream.next_in, output, len) } let data = unsafe { core::slice::from_raw_parts(output, len) }; stream.state.crc_fold.fold(data, 0); } else if stream.state.wrap == 1 { // we cannot fuse the adler and the copy in our case, because adler32 takes a slice. // Another process is allowed to concurrently modify stream.next_in, so we cannot turn it // into a rust slice (violates its safety requirements) unsafe { core::ptr::copy_nonoverlapping(stream.next_in, output, len) } let data = unsafe { core::slice::from_raw_parts(output, len) }; stream.adler = crate::adler32::adler32(stream.adler as u32, data) as _; } else { unsafe { core::ptr::copy_nonoverlapping(stream.next_in, output, len) } } stream.next_in = stream.next_in.wrapping_add(len); stream.total_in += len as crate::c_api::z_size; stream.next_out = stream.next_out.wrapping_add(len as _); stream.avail_out = stream.avail_out.wrapping_sub(len as _); stream.total_out = stream.total_out.wrapping_add(len as _); len }