Source code
Revision control
Copy as Markdown
Other Tools
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 {
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 */
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;
state
.window
.filled_mut()
.copy_within(state.w_size..state.w_size + state.strstart, 0);
if state.matches < 2 {
state.matches += 1; /* add a pending slide_hash() */
}
state.insert = Ord::min(state.insert, state.strstart);
}
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;
state
.window
.filled_mut()
.copy_within(state.w_size..state.w_size + state.strstart, 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;
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
}