mod algorithm; mod compare256; mod hash_calc; mod longest_match; mod pending; mod slide_hash; mod sym_buf; mod trees_tbl; mod window;
// Position relative to the current window pub(crate) type Pos = u16;
// SAFETY: This struct must have the same layout as [`z_stream`], so that casts and transmutations // between the two can work without UB. #[repr(C)] pubstruct DeflateStream<'a> { pub(crate) next_in: *mutcrate::c_api::Bytef, pub(crate) avail_in: crate::c_api::uInt, pub(crate) total_in: crate::c_api::z_size, pub(crate) next_out: *mutcrate::c_api::Bytef, pub(crate) avail_out: crate::c_api::uInt, pub(crate) total_out: crate::c_api::z_size, pub(crate) msg: *const core::ffi::c_char, pub(crate) state: &'a mut State<'a>, pub(crate) alloc: Allocator<'a>, pub(crate) data_type: core::ffi::c_int, pub(crate) adler: crate::c_api::z_checksum, pub(crate) reserved: crate::c_api::uLong,
}
unsafeimpl Sync for DeflateStream<'_> {} unsafeimpl Send for DeflateStream<'_> {}
impl<'a> DeflateStream<'a> { // z_stream and DeflateStream must have the same layout. Do our best to check if this is true. // (imperfect check, but should catch most mistakes.) const _S: () = assert!(core::mem::size_of::<z_stream>() == core::mem::size_of::<Self>()); const _A: () = assert!(core::mem::align_of::<z_stream>() == core::mem::align_of::<Self>());
/// # Safety /// /// Behavior is undefined if any of the following conditions are violated: /// /// - `strm` satisfies the conditions of [`pointer::as_mut`] /// - if not `NULL`, `strm` as initialized using [`init`] or similar /// /// [`pointer::as_mut`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.as_mut #[inline(always)] pubunsafefn from_stream_mut(strm: *mut z_stream) -> Option<&'a mut Self> {
{ // Safety: ptr points to a valid value of type z_stream (if non-null) let stream = unsafe { strm.as_ref() }?;
if stream.zalloc.is_none() || stream.zfree.is_none() { return None;
}
if stream.state.is_null() { return None;
}
}
// SAFETY: DeflateStream has an equivalent layout as z_stream unsafe { strm.cast::<DeflateStream>().as_mut() }
}
/// # Safety /// /// Behavior is undefined if any of the following conditions are violated: /// /// - `strm` satisfies the conditions of [`pointer::as_ref`] /// - if not `NULL`, `strm` as initialized using [`init`] or similar /// /// [`pointer::as_ref`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.as_ref #[inline(always)] pubunsafefn from_stream_ref(strm: *const z_stream) -> Option<&'a Self> {
{ // Safety: ptr points to a valid value of type z_stream (if non-null) let stream = unsafe { strm.as_ref() }?;
if stream.zalloc.is_none() || stream.zfree.is_none() { return None;
}
if stream.state.is_null() { return None;
}
}
// SAFETY: DeflateStream has an equivalent layout as z_stream unsafe { strm.cast::<DeflateStream>().as_ref() }
}
fn as_z_stream_mut(&mutself) -> &mut z_stream { // SAFETY: a valid &mut DeflateStream is also a valid &mut z_stream unsafe { &mut *(selfas *mut DeflateStream as *mut z_stream) }
}
fn try_from(value: i32) -> Result<Self, Self::Error> { match value { 8 => Ok(Self::Deflated),
_ => Err(()),
}
}
}
/// Configuration for compression. /// /// Used with [`compress_slice`]. /// /// In most cases only the compression level is relevant. We provide three profiles: /// /// - [`DeflateConfig::best_speed`] provides the fastest compression (at the cost of compression /// quality) /// - [`DeflateConfig::default`] tries to find a happy middle /// - [`DeflateConfig::best_compression`] provides the best compression (at the cost of longer /// runtime) #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "__internal-fuzz", derive(arbitrary::Arbitrary))] pubstruct DeflateConfig { pub level: i32, pub method: Method, pub window_bits: i32, pub mem_level: i32, pub strategy: Strategy,
}
#[cfg(any(test, feature = "__internal-test"))] impl quickcheck::Arbitrary for DeflateConfig { fn arbitrary(g: &mut quickcheck::Gen) -> Self { let mem_levels: Vec<_> = (1..=9).collect(); let levels: Vec<_> = (0..=9).collect();
/* Todo: ignore strm->next_in if we use it as window */
stream.msg = core::ptr::null_mut();
// for safety we must really make sure that alloc and free are consistent // this is a (slight) deviation from stock zlib. In this crate we pick the rust // allocator as the default, but `libz-rs-sys` always explicitly sets an allocator, // and can configure the C allocator #[cfg(feature = "rust-allocator")] if stream.zalloc.is_none() || stream.zfree.is_none() {
stream.configure_default_rust_allocator()
}
#[cfg(feature = "c-allocator")] if stream.zalloc.is_none() || stream.zfree.is_none() {
stream.configure_default_c_allocator()
}
if stream.zalloc.is_none() || stream.zfree.is_none() { return ReturnCode::StreamError;
}
if level == crate::c_api::Z_DEFAULT_COMPRESSION {
level = 6;
}
let wrap = if window_bits < 0 { if window_bits < -MAX_WBITS { return ReturnCode::StreamError;
}
window_bits = -window_bits;
let lit_bufsize = 1 << (mem_level + 6); // 16K elements by default let allocs = DeflateAllocOffsets::new(window_bits, lit_bufsize);
// FIXME: pointer methods on NonNull are stable since 1.80.0 let Some(allocation_start) = alloc.allocate_slice_raw::<u8>(allocs.total_size) else { return ReturnCode::MemError;
};
let w_size = 1 << window_bits; let align_offset = (allocation_start.as_ptr() as usize).next_multiple_of(64)
- (allocation_start.as_ptr() as usize); let buf = unsafe { allocation_start.as_ptr().add(align_offset) };
let (window, prev, head, pending, sym_buf) = unsafe { let window_ptr: *mut u8 = buf.add(allocs.window_pos);
window_ptr.write_bytes(0u8, 2 * w_size); let window = Window::from_raw_parts(window_ptr, window_bits);
// FIXME: write_bytes is stable for NonNull since 1.80.0 let prev_ptr = buf.add(allocs.prev_pos).cast::<Pos>();
prev_ptr.write_bytes(0, w_size); let prev = WeakSliceMut::from_raw_parts_mut(prev_ptr, w_size);
let head_ptr = buf.add(allocs.head_pos).cast::<[Pos; HASH_SIZE]>(); // Zero out the full array. `write_bytes` will write `1 * size_of<[Pos; HASH_SIZE]>` bytes.
head_ptr.write_bytes(0, 1); let head = WeakArrayMut::<Pos, HASH_SIZE>::from_ptr(head_ptr);
let pending_ptr = buf.add(allocs.pending_pos).cast::<MaybeUninit<u8>>(); let pending = Pending::from_raw_parts(pending_ptr, 4 * lit_bufsize);
let sym_buf_ptr = buf.add(allocs.sym_buf_pos); let sym_buf = SymBuf::from_raw_parts(sym_buf_ptr, lit_bufsize);
// just provide a valid default; gets set properly later
hash_calc_variant: HashCalcVariant::Standard,
_cache_line_0: (),
_cache_line_1: (),
_cache_line_2: (),
_cache_line_3: (),
_padding_0: [0; 16],
};
let Some(stream) = (unsafe { DeflateStream::from_stream_mut(stream) }) else { if cfg!(debug_assertions) {
unreachable!("we should have initialized the stream properly");
} return ReturnCode::StreamError;
};
if !(0..=9).contains(&level) { return ReturnCode::StreamError;
}
let level = level as i8;
let func = CONFIGURATION_TABLE[stream.state.level as usize].func;
let state = &mut stream.state;
// FIXME: use fn_addr_eq when it's available in our MSRV. The comparison returning false here // is not functionally incorrect, but would be inconsistent with zlib-ng. #[allow(unpredictable_function_pointer_comparisons)] if (strategy != state.strategy || func != CONFIGURATION_TABLE[level as usize].func)
&& state.last_flush != -2
{ // Flush the last buffer. let err = deflate(stream, DeflateFlush::Block); if err == ReturnCode::StreamError { return err;
}
let state = &mut stream.state;
if stream.avail_in != 0
|| ((state.strstart as isize - state.block_start) + state.lookahead as isize) != 0
{ return ReturnCode::BufError;
}
}
let state = &mut stream.state;
if state.level != level { if state.level == 0 && state.matches != 0 { if state.matches == 1 { self::slide_hash::slide_hash(state);
} else {
state.head.as_mut_slice().fill(0);
}
state.matches = 0;
}
lm_set_level(state, level);
}
state.strategy = strategy;
ReturnCode::Ok
}
pubfn set_dictionary(stream: &mut DeflateStream, mut dictionary: &[u8]) -> ReturnCode { let state = &mut stream.state;
// when using zlib wrappers, compute Adler-32 for provided dictionary if wrap == 1 {
stream.adler = adler32(stream.adler as u32, dictionary) as z_checksum;
}
// avoid computing Adler-32 in read_buf
state.wrap = 0;
// if dictionary would fill window, just replace the history if dictionary.len() >= state.window.capacity() { if wrap == 0 { // clear the hash table
state.head.as_mut_slice().fill(0);
// use the tail
dictionary = &dictionary[dictionary.len() - state.w_size..];
}
// insert dictionary into window and hash let avail = stream.avail_in; let next = stream.next_in;
stream.avail_in = dictionary.len() as _;
stream.next_in = dictionary.as_ptr() as *mut u8;
fill_window(stream);
while stream.state.lookahead >= STD_MIN_MATCH { let str = stream.state.strstart; let n = stream.state.lookahead - (STD_MIN_MATCH - 1);
stream.state.insert_string(str, n);
stream.state.strstart = str + n;
stream.state.lookahead = STD_MIN_MATCH - 1;
fill_window(stream);
}
state.bit_writer.bits_used += put as u8;
state.bit_writer.flush_bits();
value64 >>= put;
bits -= put;
if bits == 0 { break;
}
}
ReturnCode::Ok
}
pubfn copy<'a>(
dest: &mut MaybeUninit<DeflateStream<'a>>,
source: &mut DeflateStream<'a>,
) -> ReturnCode { let w_size = source.state.w_size; let window_bits = source.state.w_bits() as usize; let lit_bufsize = source.state.lit_bufsize;
// SAFETY: source and dest are both mutable references, so guaranteed not to overlap. // dest being a reference to maybe uninitialized memory makes a copy of 1 DeflateStream valid. unsafe { core::ptr::copy_nonoverlapping(source, dest.as_mut_ptr(), 1) };
let source_state = &source.state; let alloc = &source.alloc;
let allocs = DeflateAllocOffsets::new(window_bits, lit_bufsize);
let Some(allocation_start) = alloc.allocate_slice_raw::<u8>(allocs.total_size) else { return ReturnCode::MemError;
};
let align_offset = (allocation_start.as_ptr() as usize).next_multiple_of(64)
- (allocation_start.as_ptr() as usize); let buf = unsafe { allocation_start.as_ptr().add(align_offset) };
let (window, prev, head, pending, sym_buf) = unsafe { let window_ptr: *mut u8 = buf.add(allocs.window_pos);
window_ptr
.copy_from_nonoverlapping(source_state.window.as_ptr(), source_state.window.capacity()); let window = Window::from_raw_parts(window_ptr, window_bits);
// FIXME: write_bytes is stable for NonNull since 1.80.0 let prev_ptr = buf.add(allocs.prev_pos).cast::<Pos>();
prev_ptr.copy_from_nonoverlapping(source_state.prev.as_ptr(), source_state.prev.len()); let prev = WeakSliceMut::from_raw_parts_mut(prev_ptr, w_size);
// zero out head's first element let head_ptr = buf.add(allocs.head_pos).cast::<[Pos; HASH_SIZE]>();
head_ptr.copy_from_nonoverlapping(source_state.head.as_ptr(), 1); let head = WeakArrayMut::<Pos, HASH_SIZE>::from_ptr(head_ptr);
let pending_ptr = buf.add(allocs.pending_pos); let pending = source_state.bit_writer.pending.clone_to(pending_ptr);
let sym_buf_ptr = buf.add(allocs.sym_buf_pos); let sym_buf = source_state.sym_buf.clone_to(sym_buf_ptr);
// write the cloned state into state_ptr let state_allocation = unsafe { buf.add(allocs.state_pos).cast::<State>() }; unsafe { state_allocation.write(dest_state) }; // FIXME: write is stable for NonNull since 1.80.0
// insert the state_ptr into `dest` let field_ptr = unsafe { core::ptr::addr_of_mut!((*dest.as_mut_ptr()).state) }; unsafe { core::ptr::write(field_ptr as *mut *mut State, state_allocation) };
// update the gzhead field (it contains a mutable reference so we need to be careful let field_ptr = unsafe { core::ptr::addr_of_mut!((*dest.as_mut_ptr()).state.gzhead) }; unsafe { core::ptr::copy(&source_state.gzhead, field_ptr, 1) };
ReturnCode::Ok
}
/// # Returns /// /// - Err when deflate is not done. A common cause is insufficient output space /// - Ok otherwise pubfn end<'a>(stream: &'a mut DeflateStream) -> Result<&'a mut z_stream, &'a mut z_stream> { let status = stream.state.status; let allocation_start = stream.state.allocation_start; let total_allocation_size = stream.state.total_allocation_size; let alloc = stream.alloc;
let stream = stream.as_z_stream_mut(); let _ = core::mem::replace(&mut stream.state, core::ptr::null_mut());
/// total bit length of compressed file (NOTE: zlib-ng uses a 32-bit integer here) #[cfg(feature = "ZLIB_DEBUG")]
compressed_len: usize, /// bit length of compressed data sent (NOTE: zlib-ng uses a 32-bit integer here) #[cfg(feature = "ZLIB_DEBUG")]
bits_sent: usize,
}
/* Send the length code, len is the match length - STD_MIN_MATCH */ let code = self::trees_tbl::LENGTH_CODE[lc] as usize; let c = code + LITERALS + 1;
assert!(c < L_CODES, "bad l_code"); // send_code_trace(s, c);
let lnode = ltree[c]; letmut match_bits: u64 = lnode.code() as u64; letmut match_bits_len = lnode.len() as usize; let extra = StaticTreeDesc::EXTRA_LBITS[code] as usize; if extra != 0 {
lc -= self::trees_tbl::BASE_LENGTH[code] as usize;
match_bits |= (lc as u64) << match_bits_len;
match_bits_len += extra;
}
(match_bits, match_bits_len)
}
#[inline] constfn encode_dist(dtree: &[Value], mut dist: u16) -> (u64, usize) {
dist -= 1; /* dist is now the match distance - 1 */ let code = State::d_code(dist as usize) as usize;
assert!(code < D_CODES, "bad d_code"); // send_code_trace(s, code);
/* Send the distance code */ let dnode = dtree[code]; letmut match_bits = dnode.code() as u64; letmut match_bits_len = dnode.len() as usize; let extra = StaticTreeDesc::EXTRA_DBITS[code] as usize; if extra != 0 {
dist -= self::trees_tbl::BASE_DIST[code];
match_bits |= (dist as u64) << match_bits_len;
match_bits_len += extra;
}
fn flush_bits(&mutself) {
debug_assert!(self.bits_used <= 64); let removed = self.bits_used.saturating_sub(7).next_multiple_of(8); let keep_bytes = self.bits_used / 8; // can never divide by zero
let src = &self.bit_buffer.to_le_bytes(); self.pending.extend(&src[..keep_bytes as usize]);
self.bits_used -= removed; self.bit_buffer = self.bit_buffer.checked_shr(removed as u32).unwrap_or(0);
}
fn emit_align(&mutself) {
debug_assert!(self.bits_used <= 64); let keep_bytes = self.bits_used.div_ceil(8); let src = &self.bit_buffer.to_le_bytes(); self.pending.extend(&src[..keep_bytes as usize]);
self.bits_used = 0; self.bit_buffer = 0;
self.sent_bits_align();
}
fn send_bits_trace(&self, _value: u64, _len: u8) {
trace!(" l {:>2} v {:>4x} ", _len, _value);
}
fn send_code(&mutself, code: usize, tree: &[Value]) { let node = tree[code]; self.send_bits(node.code() as u64, node.len() as u8)
}
/// Send one empty static block to give enough lookahead for inflate. /// This takes 10 bits, of which 7 may remain in the bit buffer. pubfn align(&mutself) { self.emit_tree(BlockType::StaticTrees, false); self.emit_end_block(&STATIC_LTREE, false); self.flush_bits();
}
pub(crate) fn emit_tree(&mutself, block_type: BlockType, is_last_block: bool) { let header_bits = ((block_type as u64) << 1) | (is_last_block as u64); self.send_bits(header_bits, 3);
trace!("\n--- Emit Tree: Last: {}\n", is_last_block as u8);
}
fn compress_block_help(&mutself, sym_buf: &SymBuf, ltree: &[Value], dtree: &[Value]) { for (dist, lc) in sym_buf.iter() { match dist { 0 => self.emit_lit(ltree, lc) as usize,
_ => self.emit_dist(ltree, dtree, lc, dist),
};
}
self.emit_end_block(ltree, false)
}
fn send_tree(&mutself, tree: &[Value], bl_tree: &[Value], max_code: usize) { /* tree: the tree to be scanned */ /* max_code and its largest code of non zero frequency */ letmut prevlen: isize = -1; /* last emitted length */ letmut curlen; /* length of current code */ letmut nextlen = tree[0].len(); /* length of next code */ letmut count = 0; /* repeat count of the current code */ letmut max_count = 7; /* max repeat count */ letmut min_count = 4; /* min repeat count */
/// Whether or not a block is currently open for the QUICK deflation scheme. /// 0 if the block is closed, 1 if there is an active block, or 2 if there /// is an active block and it is the last block. pub(crate) block_open: u8,
pub(crate) hash_calc_variant: HashCalcVariant,
pub(crate) match_available: bool, /* set if previous match exists */
/// Use a faster search when the previous match is longer than this pub(crate) good_match: u16,
/// Stop searching when current match exceeds this pub(crate) nice_match: u16,
pub(crate) match_start: Pos, /* start of matching string */ pub(crate) prev_match: Pos, /* previous match */ pub(crate) strstart: usize, /* start of string to insert */
pub(crate) lookahead: usize, /* number of valid bytes ahead in window */
_cache_line_0: (),
/// prev[N], where N is an offset in the current window, contains the offset in the window /// of the previous 4-byte sequence that hashes to the same value as the 4-byte sequence /// starting at N. Together with head, prev forms a chained hash table that can be used /// to find earlier strings in the window that are potential matches for new input being /// deflated. pub(crate) prev: WeakSliceMut<'a, u16>, /// head[H] contains the offset of the last 4-character sequence seen so far in /// the current window that hashes to H (as calculated using the hash_calc_variant). pub(crate) head: WeakArrayMut<'a, u16, HASH_SIZE>,
/// Length of the best match at previous step. Matches not greater than this /// are discarded. This is used in the lazy match evaluation. pub(crate) prev_length: u16,
/// To speed up deflation, hash chains are never searched beyond this length. /// A higher limit improves compression ratio but degrades the speed. pub(crate) max_chain_length: u16,
// TODO untangle this mess! zlib uses the same field differently based on compression level // we should just have 2 fields for clarity! // // Insert new strings in the hash table only if the match length is not // greater than this length. This saves time but degrades compression. // max_insert_length is used only for compression levels <= 6. // define max_insert_length max_lazy_match /// Attempt to find a better match only when the current match is strictly smaller /// than this value. This mechanism is used only for compression levels >= 4. pub(crate) max_lazy_match: u16,
/// number of string matches in current block /// NOTE: this is a saturating 8-bit counter, to help keep the struct compact. The code that /// makes decisions based on this field only cares whether the count is greater than 2, so /// an 8-bit counter is sufficient. pub(crate) matches: u8,
/// Window position at the beginning of the current output block. Gets /// negative when the window is moved backwards. pub(crate) block_start: isize,
pub(crate) sym_buf: SymBuf<'a>,
_cache_line_1: (),
/// Size of match buffer for literals/lengths. There are 4 reasons for /// limiting lit_bufsize to 64K: /// - frequencies can be kept in 16 bit counters /// - if compression is not successful for the first block, all input /// data is still in the window so we can still emit a stored block even /// when input comes from standard input. (This can also be done for /// all blocks if lit_bufsize is not greater than 32K.) /// - if compression is not successful for a file smaller than 64K, we can /// even emit a stored file instead of a stored block (saving 5 bytes). /// This is applicable only for zip (not gzip or zlib). /// - creating new Huffman trees less frequently may not provide fast /// adaptation to changes in the input data statistics. (Take for /// example a binary file with poorly compressible code followed by /// a highly compressible string table.) Smaller buffer sizes give /// fast adaptation but have of course the overhead of transmitting /// trees more frequently. /// - I can't count above 4
lit_bufsize: usize,
/// Actual size of window: 2*w_size, except when the user input buffer is directly used as sliding window. pub(crate) window_size: usize,
bit_writer: BitWriter<'a>,
_cache_line_2: (),
/// bit length of current block with optimal trees
opt_len: usize, /// bit length of current block with static trees
static_len: usize,
/// bytes at end of window left to insert pub(crate) insert: usize,
/// hash index of string to be inserted pub(crate) ins_h: u32,
/// The (unaligned) address of the state allocation that can be passed to zfree.
allocation_start: NonNull<u8>, /// Total size of the allocation in bytes.
total_allocation_size: usize,
l_desc: TreeDesc<HEAP_SIZE>, /* literal and length tree */
d_desc: TreeDesc<{ 2 * D_CODES + 1 }>, /* distance tree */
bl_desc: TreeDesc<{ 2 * BL_CODES + 1 }>, /* Huffman tree for bit lengths */
}
// TODO untangle this mess! zlib uses the same field differently based on compression level // we should just have 2 fields for clarity! pub(crate) fn max_insert_length(&self) -> usize { self.max_lazy_match as usize
}
/// Total size of the pending buf. But because `pending` shares memory with `sym_buf`, this is /// not the number of bytes that are actually in `pending`! pub(crate) fn pending_buf_size(&self) -> usize { self.lit_bufsize * 4
}
// This helper is to work around an ownership issue in algorithm/medium. pub(crate) fn tally_lit_help(
sym_buf: &mut SymBuf,
l_desc: &mut TreeDesc<HEAP_SIZE>,
unmatched: u8,
) -> bool { const _VERIFY: () = { // Verify during compilation that even the largest possible value // of unmatched will fit within the expected range.
assert!(
u8::MAX as usize <= STD_MAX_MATCH - STD_MIN_MATCH, "tally_lit: bad literal"
);
};
sym_buf.push_lit(unmatched);
*l_desc.dyn_tree[unmatched as usize].freq_mut() += 1;
// signal that the current block should be flushed
sym_buf.should_flush_block()
}
constfn d_code(dist: usize) -> u8 { const _VERIFY: () = { // Verify during compilation that every DIST_CODE value is < D_CODES. letmut i = 0; while i < trees_tbl::DIST_CODE.len() {
assert!(trees_tbl::DIST_CODE[i] < D_CODES as u8);
i += 1;
}
};
let index = if dist < 256 { dist } else { 256 + (dist >> 7) }; self::trees_tbl::DIST_CODE[index]
}
#[inline(always)] pub(crate) fn tally_dist(&mutself, mut dist: usize, len: usize) -> bool { self.sym_buf.push_dist(dist as u16, len as u8);
#[derive(Debug)] pub(crate) enum BlockState { /// block not completed, need more input or more output
NeedMore = 0, /// block flush performed
BlockDone = 1, /// finish started, need only more output at next deflate
FinishStarted = 2, /// finish done, accept no more input or output
FinishDone = 3,
}
// Maximum stored block length in deflate format (not including header). pub(crate) const MAX_STORED: usize = 65535; // so u16::max
pub(crate) fn read_buf_window(stream: &mut DeflateStream, offset: usize, size: usize) -> usize { let len = Ord::min(stream.avail_in as usize, size);
if len == 0 { return0;
}
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! let window = &mut stream.state.window; // SAFETY: len is bounded by avail_in, so this copy is in bounds. unsafe { window.copy_and_initialize(offset..offset + len, stream.next_in) };
let data = &stream.state.window.filled()[offset..][..len];
stream.state.crc_fold.fold(data, CRC32_INITIAL_VALUE);
} elseif stream.state.wrap == 1 { // we likely cannot fuse the adler32 and the copy here because the input can be changed by // a concurrent thread. Therefore it cannot be converted into a slice! let window = &mut stream.state.window; // SAFETY: len is bounded by avail_in, so this copy is in bounds. unsafe { window.copy_and_initialize(offset..offset + len, stream.next_in) };
let data = &stream.state.window.filled()[offset..][..len];
stream.adler = adler32(stream.adler as u32, data) as _;
} else { let window = &mut stream.state.window; // SAFETY: len is bounded by avail_in, so this copy is in bounds. unsafe { window.copy_and_initialize(offset..offset + len, stream.next_in) };
}
// These can overflow, especially on windows where the integer type is u32 and input/output are // larger than 4GB.
stream.next_in = stream.next_in.wrapping_add(len);
stream.total_in = stream.total_in.wrapping_add(len ascrate::c_api::z_size);
// align on byte boundary
state.bit_writer.emit_align();
state.bit_writer.cmpr_bits_align();
let input_block: &[u8] = &state.window.filled()[window_range]; let stored_len = input_block.len() as u16;
state.bit_writer.pending.extend(&stored_len.to_le_bytes());
state
.bit_writer
.pending
.extend(&(!stored_len).to_le_bytes());
state.bit_writer.cmpr_bits_add(32);
state.bit_writer.sent_bits_add(32); if stored_len > 0 {
state.bit_writer.pending.extend(input_block);
state.bit_writer.cmpr_bits_add((stored_len << 3) as usize);
state.bit_writer.sent_bits_add((stored_len << 3) as usize);
}
}
/// The minimum match length mandated by the deflate standard pub(crate) const STD_MIN_MATCH: usize = 3; /// The maximum match length mandated by the deflate standard pub(crate) const STD_MAX_MATCH: usize = 258;
/// The minimum wanted match length, affects deflate_quick, deflate_fast, deflate_medium and deflate_slow pub(crate) const WANT_MIN_MATCH: usize = 4;
loop { let state = &mut *stream.state; letmut more = state.window_size - state.lookahead - state.strstart;
// If the window is almost full and there is insufficient lookahead, // move the upper half to the lower one to make room in the upper half. if state.strstart >= wsize + state.max_dist() { // shift the window to the left let (old, new) = state.window.filled_mut()[..2 * wsize].split_at_mut(wsize);
old.copy_from_slice(new);
if state.match_start >= wsize as u16 {
state.match_start -= wsize as u16;
} else {
state.match_start = 0;
state.prev_length = 0;
}
state.strstart -= wsize; /* we now have strstart >= MAX_DIST */
state.block_start = state.block_start.wrapping_sub_unsigned(wsize);
state.insert = Ord::min(state.insert, state.strstart);
self::slide_hash::slide_hash(state);
more += wsize;
}
if stream.avail_in == 0 { break;
}
// If there was no sliding: // strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && // more == window_size - lookahead - strstart // => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) // => more >= window_size - 2*WSIZE + 2 // In the BIG_MEM or MMAP case (not yet supported), // window_size == input_size + MIN_LOOKAHEAD && // strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. // Otherwise, window_size == 2*WSIZE so more >= 2. // If there was sliding, more >= WSIZE. So in all cases, more >= 2.
assert!(more >= 2, "more < 2");
let n = read_buf_window(stream, stream.state.strstart + stream.state.lookahead, more);
let state = &mut *stream.state;
state.lookahead += n;
// Initialize the hash value now that we have some input: if state.lookahead + state.insert >= STD_MIN_MATCH { let string = state.strstart - state.insert; if state.max_chain_length > 1024 { let v0 = state.window.filled()[string] as u32; let v1 = state.window.filled()[string + 1] as u32;
state.ins_h = state.update_hash(v0, v1);
} elseif string >= 1 {
state.quick_insert_string(string + 2 - STD_MIN_MATCH);
} letmut count = state.insert; if state.lookahead == 1 {
count -= 1;
} if count > 0 {
state.insert_string(string, count);
state.insert -= count;
}
}
// If the whole input has less than STD_MIN_MATCH bytes, ins_h is garbage, // but this is not important since only literal bytes will be emitted.
pub(crate) struct StaticTreeDesc { /// static tree or NULL pub(crate) static_tree: &'static [Value], /// extra bits for each code or NULL
extra_bits: &'static [u8], /// base index for extra_bits
extra_base: usize, /// max number of elements in the tree
elems: usize, /// max bit length for the codes
max_length: u16,
}
/// extra bits for each bit length code const EXTRA_BLBITS: [u8; BL_CODES] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7];
/// The lengths of the bit length codes are sent in order of decreasing /// probability, to avoid transmitting the lengths for unused bit length codes. const BL_ORDER: [u8; BL_CODES] = [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15,
];
fn build_tree<const N: usize>(state: &mut State, desc: &mut TreeDesc<N>) { let tree = &mut desc.dyn_tree; let stree = desc.stat_desc.static_tree; let elements = desc.stat_desc.elems;
// The pkzip format requires that at least one distance code exists, // and that at least one bit should be sent even if there is only one // possible code. So to avoid special checks later on we force at least // two codes of non zero frequency. while heap.heap_len < 2 {
heap.heap_len += 1; let node = if max_code < 2 {
max_code += 1;
max_code
} else { 0
};
debug_assert!(node >= 0); let node = node as usize;
heap.heap[heap.heap_len] = node as u32;
*tree[node].freq_mut() = 1;
heap.depth[node] = 0;
state.opt_len -= 1; if !stree.is_empty() {
state.static_len -= stree[node].len() as usize;
} /* node is 0 or 1 so it does not have extra bits */
}
debug_assert!(max_code >= 0); let max_code = max_code as usize;
desc.max_code = max_code;
// The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, // establish sub-heaps of increasing lengths: letmut n = heap.heap_len / 2; while n >= 1 {
heap.pqdownheap(tree, n);
n -= 1;
}
heap.construct_huffman_tree(tree, elements);
// At this point, the fields freq and dad are set. We can now // generate the bit lengths. let bl_count = gen_bitlen(state, &mut heap, desc);
// The field len is now set, we can generate the bit codes
gen_codes(&mut desc.dyn_tree, max_code, &bl_count);
}
fn gen_bitlen<const N: usize>(
state: &mut State,
heap: &mut Heap,
desc: &mut TreeDesc<N>,
) -> [u16; MAX_BITS + 1] { let tree = &mut desc.dyn_tree; let max_code = desc.max_code; let stree = desc.stat_desc.static_tree; let extra = desc.stat_desc.extra_bits; let base = desc.stat_desc.extra_base; let max_length = desc.stat_desc.max_length;
letmut bl_count = [0u16; MAX_BITS + 1];
// In a first pass, compute the optimal bit lengths (which may // overflow in the case of the bit length tree).
*tree[heap.heap[heap.heap_max] as usize].len_mut() = 0; /* root of the heap */
// number of elements with bit length too large letmut overflow: i32 = 0;
for h in heap.heap_max + 1..HEAP_SIZE { let n = heap.heap[h] as usize; letmut bits = tree[tree[n].dad() as usize].len() + 1;
// We overwrite tree[n].Dad which is no longer needed
*tree[n].len_mut() = bits;
// not a leaf node if n > max_code { continue;
}
bl_count[bits as usize] += 1; letmut xbits = 0; if n >= base {
xbits = extra[n - base] as usize;
}
let f = tree[n].freq() as usize;
state.opt_len += f * (bits as usize + xbits);
if !stree.is_empty() {
state.static_len += f * (stree[n].len() as usize + xbits);
}
}
if overflow == 0 { return bl_count;
}
/* Find the first bit length which could increase: */ loop { letmut bits = max_length as usize - 1; while bl_count[bits] == 0 {
bits -= 1;
}
bl_count[bits] -= 1; /* move one leaf down the tree */
bl_count[bits + 1] += 2; /* move one overflow item as its brother */
bl_count[max_length as usize] -= 1; /* The brother of the overflow item also moves one step up, *butthisdoesnotaffectbl_count[max_length]
*/
overflow -= 2;
if overflow <= 0 { break;
}
}
// Now recompute all bit lengths, scanning in increasing frequency. // h is still equal to HEAP_SIZE. (It is simpler to reconstruct all // lengths instead of fixing only the wrong ones. This idea is taken // from 'ar' written by Haruhiko Okumura.) letmut h = HEAP_SIZE; for bits in (1..=max_length).rev() { letmut n = bl_count[bits as usize]; while n != 0 {
h -= 1; let m = heap.heap[h] as usize; if m > max_code { continue;
}
if tree[m].len() != bits { // Tracev((stderr, "code %d bits %d->%u\n", m, tree[m].Len, bits));
state.opt_len += (bits * tree[m].freq()) as usize;
state.opt_len -= (tree[m].len() * tree[m].freq()) as usize;
*tree[m].len_mut() = bits;
}
n -= 1;
}
}
bl_count
}
/// Checks that symbol is a printing character (excluding space) #[allow(unused)] fn isgraph(c: u8) -> bool {
(c > 0x20) && (c <= 0x7E)
}
fn gen_codes(tree: &mut [Value], max_code: usize, bl_count: &[u16]) { /* tree: the tree to decorate */ /* max_code: largest code with non zero frequency */ /* bl_count: number of codes at each bit length */ letmut next_code = [0; MAX_BITS + 1]; /* next code value for each bit length */ letmut code = 0; /* running code value */
/* The distribution counts are first used to generate the code values *withoutbitreversal.
*/ for bits in1..=MAX_BITS {
code = (code + bl_count[bits - 1]) << 1;
next_code[bits] = code;
}
/* Check that the bit counts in bl_count are consistent. The last code *mustbeallones.
*/
assert!(
code + bl_count[MAX_BITS] - 1 == (1 << MAX_BITS) - 1, "inconsistent bit counts"
);
trace!("\ngen_codes: max_code {max_code} ");
for n in0..=max_code { let len = tree[n].len(); if len == 0 { continue;
}
/* Now reverse the bits */
assert!((1..=15).contains(&len), "code length must be 1-15");
*tree[n].code_mut() = next_code[len as usize].reverse_bits() >> (16 - len);
next_code[len as usize] += 1;
if tree != self::trees_tbl::STATIC_LTREE.as_slice() {
trace!( "\nn {:>3} {} l {:>2} c {:>4x} ({:x}) ",
n, if isgraph(n as u8) {
char::from_u32(n as u32).unwrap()
} else { ' '
},
len,
tree[n].code(),
next_code[len as usize] - 1
);
}
}
}
/// repeat previous bit length 3-6 times (2 bits of repeat count) const REP_3_6: usize = 16;
/// repeat a zero length 3-10 times (3 bits of repeat count) const REPZ_3_10: usize = 17;
/// repeat a zero length 11-138 times (7 bits of repeat count) const REPZ_11_138: usize = 18;
fn scan_tree(bl_desc: &mut TreeDesc<{ 2 * BL_CODES + 1 }>, tree: &tyle='color:red'>mut [Value], max_code: usize) { /* tree: the tree to be scanned */ /* max_code: and its largest code of non zero frequency */ letmut prevlen = -1isize; /* last emitted length */ letmut curlen: isize; /* length of current code */ letmut nextlen = tree[0].len(); /* length of next code */ letmut count = 0; /* repeat count of the current code */ letmut max_count = 7; /* max repeat count */ letmut min_count = 4; /* min repeat count */
trace!("\nbl counts: ");
state.bit_writer.send_bits(lcodes as u64 - 257, 5); /* not +255 as stated in appnote.txt */
state.bit_writer.send_bits(dcodes as u64 - 1, 5);
state.bit_writer.send_bits(blcodes as u64 - 4, 4); /* not -3 as stated in appnote.txt */
for rank in0..blcodes {
trace!("\nbl code {:>2} ", StaticTreeDesc::BL_ORDER[rank]);
state.bit_writer.send_bits(
state.bl_desc.dyn_tree[StaticTreeDesc::BL_ORDER[rank] as usize].len() as u64, 3,
);
}
trace!("\nbl tree: sent {}", state.bit_writer.bits_sent);
// literal tree
state
.bit_writer
.send_tree(&state.l_desc.dyn_tree, &state.bl_desc.dyn_tree, lcodes - 1);
trace!("\nlit tree: sent {}", state.bit_writer.bits_sent);
// distance tree
state
.bit_writer
.send_tree(&state.d_desc.dyn_tree, &state.bl_desc.dyn_tree, dcodes - 1);
trace!("\ndist tree: sent {}", state.bit_writer.bits_sent);
}
/// Construct the Huffman tree for the bit lengths and return the index in /// bl_order of the last bit length code to send. fn build_bl_tree(state: &mut State) -> usize { /* Determine the bit length frequencies for literal and distance trees */
/* opt_len now includes the length of the tree representations, except *thelengthsofthebitlengthscodesandthe5+5+4bitsforthecounts.
*/
/* Determine the number of bit length codes to send. The pkzip format *requiresthatatleast4bitlengthcodesbesent.(appnote.txtsays *3buttheactualvalueusedis4.)
*/ letmut max_blindex = BL_CODES - 1; while max_blindex >= 3 { let index = StaticTreeDesc::BL_ORDER[max_blindex] as usize; if state.bl_desc.dyn_tree[index].len() != 0 { break;
}
max_blindex -= 1;
}
/* Update opt_len to include the bit length tree and counts */
state.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4;
trace!( "\ndyn trees: dyn {}, stat {}",
state.opt_len,
state.static_len
);
max_blindex
}
fn zng_tr_flush_block(
stream: &mut DeflateStream,
window_offset: Option<usize>,
stored_len: u32,
last: bool,
) { /* window_offset: offset of the input block into the window */ /* stored_len: length of input block */ /* last: one if this is the last block for a file */
letmut opt_lenb; let static_lenb; letmut max_blindex = 0;
let state = &mut stream.state;
if state.sym_buf.is_empty() {
opt_lenb = 0;
static_lenb = 0;
state.static_len = 7;
} elseif state.level > 0 { if stream.data_type == DataType::Unknown as i32 {
stream.data_type = State::detect_data_type(&state.l_desc.dyn_tree) as i32;
}
// Build the bit length tree for the above two trees, and get the index // in bl_order of the last bit length code to send.
max_blindex = build_bl_tree(state);
// Determine the best encoding. Compute the block lengths in bytes.
opt_lenb = (state.opt_len + 3 + 7) >> 3;
static_lenb = (state.static_len + 3 + 7) >> 3;
if static_lenb <= opt_lenb || state.strategy == Strategy::Fixed {
opt_lenb = static_lenb;
}
} else {
assert!(window_offset.is_some(), "lost buf"); /* force a stored block */
opt_lenb = stored_len as usize + 5;
static_lenb = stored_len as usize + 5;
}
#[allow(clippy::unnecessary_unwrap)] if stored_len as usize + 4 <= opt_lenb && window_offset.is_some() { /* 4: two words for the lengths *Thetestbuf!=NULLisonlynecessaryifLIT_BUFSIZE>WSIZE. *Otherwisewecan'thaveprocessedmorethanWSIZEinputbytessince *thelastblockflush,becausecompressionwouldhavebeen *successful.IfLIT_BUFSIZE<=WSIZE,itisnevertoolateto *transformablockintoastoredblock.
*/ let window_offset = window_offset.unwrap(); let range = window_offset..window_offset + stored_len as usize;
zng_tr_stored_block(state, range, last);
} elseif static_lenb == opt_lenb {
state.bit_writer.emit_tree(BlockType::StaticTrees, last);
state.compress_block_static_trees(); // cmpr_bits_add(s, s.static_len);
} else {
state.bit_writer.emit_tree(BlockType::DynamicTrees, last);
send_all_trees(
state,
state.l_desc.max_code + 1,
state.d_desc.max_code + 1,
max_blindex + 1,
);
state.compress_block_dynamic_trees();
}
// TODO // This check is made mod 2^32, for files larger than 512 MB and unsigned long implemented on 32 bits. // assert_eq!(state.compressed_len, state.bits_sent, "bad compressed size");
state.init_block(); if last {
state.bit_writer.emit_align();
}
// we'll be using the pending buffer as temporary storage letmut beg = state.bit_writer.pending.pending().len(); /* start of bytes to update crc */
while state.bit_writer.pending.remaining() < bytes.len() { let copy = state.bit_writer.pending.remaining();
state.bit_writer.pending.extend(&bytes[..copy]);
stream.adler = crc32(
stream.adler as u32,
&state.bit_writer.pending.pending()[beg..],
) as z_checksum;
state.gzindex += copy;
flush_pending(stream);
state = &mut stream.state;
// could not flush all the pending output if !state.bit_writer.pending.pending().is_empty() {
state.last_flush = -1; return ControlFlow::Break(ReturnCode::Ok);
}
beg = 0;
bytes = &bytes[copy..];
}
state.bit_writer.pending.extend(bytes);
stream.adler = crc32(
stream.adler as u32,
&state.bit_writer.pending.pending()[beg..],
) as z_checksum;
state.gzindex = 0;
if stream.avail_out == 0 { let err = ReturnCode::BufError;
stream.msg = err.error_message(); return err;
}
let old_flush = stream.state.last_flush;
stream.state.last_flush = flush as i8;
/* Flush as much pending output as possible */ if !stream.state.bit_writer.pending.pending().is_empty() {
flush_pending(stream); if stream.avail_out == 0 { /* Since avail_out is 0, deflate will be called again with *moreoutputspace,butpossiblywithbothpendingand *avail_inequaltozero.Therewon'tbeanythingtodo, *butthisisnotanerrorsituationsomakesurewe *returnOKinsteadofBUF_ERRORatnextcallofdeflate:
*/
stream.state.last_flush = -1; return ReturnCode::Ok;
}
/* Make sure there is something to do and avoid duplicate consecutive *flushes.ForrepeatedanduselesscallswithZ_FINISH,wekeep *returningZ_STREAM_ENDinsteadofZ_BUF_ERROR.
*/
} elseif stream.avail_in == 0
&& rank_flush(flush as i8) <= rank_flush(old_flush)
&& flush != DeflateFlush::Finish
{ let err = ReturnCode::BufError;
stream.msg = err.error_message(); return err;
}
/* User must not provide more input after the first FINISH: */ if stream.state.status == Status::Finish && stream.avail_in != 0 { let err = ReturnCode::BufError;
stream.msg = err.error_message(); return err;
}
/* Write the header */ if stream.state.status == Status::Init && stream.state.wrap == 0 {
stream.state.status = Status::Busy;
}
if stream.state.status == Status::Init { let header = stream.state.header();
stream
.state
.bit_writer
.pending
.extend(&header.to_be_bytes());
/* Save the adler32 of the preset dictionary: */ if stream.state.strstart != 0 { let adler = stream.adler as u32;
stream.state.bit_writer.pending.extend(&adler.to_be_bytes());
}
stream.adler = ADLER32_INITIAL_VALUE as _;
stream.state.status = Status::Busy;
// compression must start with an empty pending buffer
flush_pending(stream);
if !stream.state.bit_writer.pending.pending().is_empty() {
stream.state.last_flush = -1;
if stream.state.status == Status::Extra { iflet Some(gzhead) = stream.state.gzhead.as_ref() { if !gzhead.extra.is_null() { let gzhead_extra = gzhead.extra;
let extra = unsafe {
core::slice::from_raw_parts( // SAFETY: gzindex is always less than extra_len, and the user // guarantees the pointer is valid for extra_len.
gzhead_extra.add(stream.state.gzindex),
(gzhead.extra_len & 0xffff) as usize - stream.state.gzindex,
)
};
if stream.state.status == Status::Name { iflet Some(gzhead) = stream.state.gzhead.as_ref() { if !gzhead.name.is_null() { // SAFETY: user satisfies precondition that gzhead.name is a C string. let gzhead_name = unsafe { CStr::from_ptr(gzhead.name.cast()) }; let bytes = gzhead_name.to_bytes_with_nul(); iflet ControlFlow::Break(err) = flush_bytes(stream, bytes) { return err;
}
}
stream.state.status = Status::Comment;
}
}
if stream.state.status == Status::Comment { iflet Some(gzhead) = stream.state.gzhead.as_ref() { if !gzhead.comment.is_null() { // SAFETY: user satisfies precondition that gzhead.name is a C string. let gzhead_comment = unsafe { CStr::from_ptr(gzhead.comment.cast()) }; let bytes = gzhead_comment.to_bytes_with_nul(); iflet ControlFlow::Break(err) = flush_bytes(stream, bytes) { return err;
}
}
stream.state.status = Status::Hcrc;
}
}
if stream.state.status == Status::Hcrc { iflet Some(gzhead) = stream.state.gzhead.as_ref() { if gzhead.hcrc != 0 { let bytes = (stream.adler as u16).to_le_bytes(); iflet ControlFlow::Break(err) = flush_bytes(stream, &bytes) { return err;
}
}
}
stream.state.status = Status::Busy;
// compression must start with an empty pending buffer
flush_pending(stream); if !stream.state.bit_writer.pending.pending().is_empty() {
stream.state.last_flush = -1; return ReturnCode::Ok;
}
}
// Start a new block or continue the current one. let state = &mut stream.state; if stream.avail_in != 0
|| state.lookahead != 0
|| (flush != DeflateFlush::NoFlush && state.status != Status::Finish)
{ let bstate = self::algorithm::run(stream, flush);
let state = &mut stream.state;
if matches!(bstate, BlockState::FinishStarted | BlockState::FinishDone) {
state.status = Status::Finish;
}
match bstate {
BlockState::NeedMore | BlockState::FinishStarted => { if stream.avail_out == 0 {
state.last_flush = -1; /* avoid BUF_ERROR next call, see above */
} return ReturnCode::Ok; /* If flush != Z_NO_FLUSH && avail_out == 0, the next call *ofdeflateshouldusethesameflushparametertomakesure *thattheflushiscomplete.Sowedon'thavetooutputan *emptyblockhere,thiswillbedoneatnextcall.Thisalso *ensuresthatforaverysmalloutputbuffer,weemitatmost *oneemptyblock.
*/
}
BlockState::BlockDone => { match flush {
DeflateFlush::NoFlush => unreachable!("condition of inner surrounding if"),
DeflateFlush::PartialFlush => {
state.bit_writer.align();
}
DeflateFlush::SyncFlush => { // add an empty stored block that is marked as not final. This is useful for // parallel deflate where we want to make sure the intermediate blocks are not // marked as "last block".
zng_tr_stored_block(state, 0..0, false);
}
DeflateFlush::FullFlush => { // add an empty stored block that is marked as not final. This is useful for // parallel deflate where we want to make sure the intermediate blocks are not // marked as "last block".
zng_tr_stored_block(state, 0..0, false);
state.head.as_mut_slice().fill(0); // forget history
if state.lookahead == 0 {
state.strstart = 0;
state.block_start = 0;
state.insert = 0;
}
}
DeflateFlush::Block => { /* fall through */ }
DeflateFlush::Finish => unreachable!("condition of outer surrounding if"),
}
flush_pending(stream);
if stream.avail_out == 0 {
stream.state.last_flush = -1; /* avoid BUF_ERROR at next call, see above */ return ReturnCode::Ok;
}
}
BlockState::FinishDone => { /* do nothing */ }
}
}
if flush != DeflateFlush::Finish { return ReturnCode::Ok;
}
// write the trailer if stream.state.wrap == 2 { let crc_fold = core::mem::take(&mut stream.state.crc_fold);
stream.adler = crc_fold.finish() as z_checksum;
let adler = stream.adler as u32;
stream.state.bit_writer.pending.extend(&adler.to_le_bytes());
let total_in = stream.total_in as u32;
stream
.state
.bit_writer
.pending
.extend(&total_in.to_le_bytes());
} elseif stream.state.wrap == 1 { let adler = stream.adler as u32;
stream.state.bit_writer.pending.extend(&adler.to_be_bytes());
}
flush_pending(stream);
// If avail_out is zero, the application will call deflate again to flush the rest. if stream.state.wrap > 0 {
stream.state.wrap = -stream.state.wrap; /* write the trailer only once! */
}
if stream.state.bit_writer.pending.pending().is_empty() {
assert_eq!(stream.state.bit_writer.bits_used, 0, "bi_buf not flushed"); return ReturnCode::StreamEnd;
}
ReturnCode::Ok
}
pub(crate) fn flush_pending(stream: &mut DeflateStream) { let state = &mut stream.state;
state.bit_writer.flush_bits();
let pending = state.bit_writer.pending.pending(); let len = Ord::min(pending.len(), stream.avail_out as usize);
if len == 0 { return;
}
trace!("\n[FLUSH {len} bytes]"); // SAFETY: len is min(pending, stream.avail_out), so we won't overrun next_out. unsafe { core::ptr::copy_nonoverlapping(pending.as_ptr(), stream.next_out, len) };
stream.next_out = stream.next_out.wrapping_add(len);
stream.total_out += len ascrate::c_api::z_size;
stream.avail_out -= len ascrate::c_api::uInt;
state.bit_writer.pending.advance(len);
}
/// Compresses `input` into the provided `output` buffer. /// /// Returns a subslice of `output` containing the compressed bytes and a /// [`ReturnCode`] indicating the result of the operation. Returns [`ReturnCode::BufError`] if /// there is insufficient output space. /// /// Use [`compress_bound`] for an upper bound on how large the output buffer needs to be. /// /// # Example /// /// ``` /// # use zlib_rs::*; /// # fn foo(input: &[u8]) { /// let mut buf = vec![0u8; compress_bound(input.len())]; /// let (compressed, rc) = compress_slice(&mut buf, input, DeflateConfig::default()); /// # } /// ``` pubfn compress_slice<'a>(
output: &'a mut [u8],
input: &[u8],
config: DeflateConfig,
) -> (&'a mut [u8], ReturnCode) { // SAFETY: a [u8] is a valid [MaybeUninit<u8>]. let output_uninit = unsafe {
core::slice::from_raw_parts_mut(output.as_mut_ptr() as *mut MaybeUninit<u8>, output.len())
};
// SAFETY: we have now initialized these bytes let output_slice = unsafe {
core::slice::from_raw_parts_mut(output.as_mut_ptr() as *mut u8, stream.total_out as usize)
};
// may DataError if insufficient output space iflet Some(stream) = unsafe { DeflateStream::from_stream_mut(&mut stream) } { let _ = end(stream);
}
(output_slice, return_code)
}
/// Returns the upper bound on the compressed size for an input of `source_len` bytes. /// /// When compression has this much space available, it will never fail because of insufficient /// output space. /// /// # Example /// /// ``` /// # use zlib_rs::*; /// /// assert_eq!(compress_bound(1024), 1161); /// assert_eq!(compress_bound(4096), 4617); /// assert_eq!(compress_bound(65536), 73737); /// /// # fn foo(input: &[u8]) { /// let mut buf = vec![0u8; compress_bound(input.len())]; /// let (compressed, rc) = compress_slice(&mut buf, input, DeflateConfig::default()); /// # } /// ``` pubconstfn compress_bound(source_len: usize) -> usize {
compress_bound_help(source_len, ZLIB_WRAPLEN)
}
constfn compress_bound_help(source_len: usize, wrap_len: usize) -> usize {
source_len // The source size itself */ // Always at least one byte for any input
.wrapping_add(if source_len == 0 { 1 } else { 0 }) // One extra byte for lengths less than 9
.wrapping_add(if source_len < 9 { 1 } else { 0 }) // Source encoding overhead, padded to next full byte
.wrapping_add(deflate_quick_overhead(source_len)) // Deflate block overhead bytes
.wrapping_add(DEFLATE_BLOCK_OVERHEAD) // none, zlib or gzip wrapper
.wrapping_add(wrap_len)
}
/// heap used to build the Huffman trees /// /// The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. /// The same heap array is used to build all trees. #[derive(Clone)] struct Heap {
heap: [u32; 2 * L_CODES + 1],
/// number of elements in the heap
heap_len: usize,
/// element of the largest frequency
heap_max: usize,
/// Construct the initial heap, with least frequent element in /// heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. fn initialize(&mutself, tree: &mut [Value]) -> isize { letmut max_code = -1;
self.heap_len = 0; self.heap_max = HEAP_SIZE;
for (n, node) in tree.iter_mut().enumerate() { if node.freq() > 0 { self.heap_len += 1; self.heap[self.heap_len] = n as u32;
max_code = n as isize; self.depth[n] = 0;
} else {
*node.len_mut() = 0;
}
}
max_code
}
/// Index within the heap array of least frequent node in the Huffman tree const SMALLEST: usize = 1;
fn pqdownheap(&mutself, tree: &[Value], mut k: usize) { /* tree: the tree to restore */ /* k: node to move down */
// Given the index $i of a node in the tree, pack the node's frequency and depth // into a single integer. The heap ordering logic uses a primary sort on frequency // and a secondary sort on depth, so packing both into one integer makes it // possible to sort with fewer comparison operations.
macro_rules! freq_and_depth {
($i:expr) => {
(tree[$i as usize].freq() as u32) << 8 | self.depth[$i as usize] as u32
};
}
let v = self.heap[k]; let v_val = freq_and_depth!(v); letmut j = k << 1; /* left son of k */
while j <= self.heap_len { /* Set j to the smallest of the two sons: */ letmut j_val = freq_and_depth!(self.heap[j]); if j < self.heap_len { let j1_val = freq_and_depth!(self.heap[j + 1]); if j1_val <= j_val {
j += 1;
j_val = j1_val;
}
}
/* Exit if v is smaller than both sons */ if v_val <= j_val { break;
}
/* Exchange v with the smallest son */ self.heap[k] = self.heap[j];
k = j;
/* And continue down the tree, setting j to the left son of k */
j <<= 1;
}
self.heap[k] = v;
}
/// Remove the smallest element from the heap and recreate the heap with /// one less element. Updates heap and heap_len. fn pqremove(&mutself, tree: &[Value]) -> u32 { let top = self.heap[Self::SMALLEST]; self.heap[Self::SMALLEST] = self.heap[self.heap_len]; self.heap_len -= 1;
self.pqdownheap(tree, Self::SMALLEST);
top
}
/// Construct the Huffman tree by repeatedly combining the least two frequent nodes. fn construct_huffman_tree(&mutself, tree: &mut [Value], mut node: usize) { loop { let n = self.pqremove(tree) as usize; /* n = node of least frequency */ let m = self.heap[Heap::SMALLEST] as usize; /* m = node of next least frequency */
self.heap_max -= 1; self.heap[self.heap_max] = n as u32; /* keep the nodes sorted by frequency */ self.heap_max -= 1; self.heap[self.heap_max] = m as u32;
/* Create a new node father of n and m */
*tree[node].freq_mut() = tree[n].freq() + tree[m].freq(); self.depth[node] = Ord::max(self.depth[n], self.depth[m]) + 1;
*tree[n].dad_mut() = node as u16;
*tree[m].dad_mut() = node as u16;
/* and insert the new node in the heap */ self.heap[Heap::SMALLEST] = node as u32;
node += 1;
/// # Safety /// /// The caller must guarantee: /// /// * If `head` is `Some` /// - `head.extra` is `NULL` or is readable for at least `head.extra_len` bytes /// - `head.name` is `NULL` or satisfies the requirements of [`core::ffi::CStr::from_ptr`] /// - `head.comment` is `NULL` or satisfies the requirements of [`core::ffi::CStr::from_ptr`] pubunsafefn set_header<'a>(
stream: &mut DeflateStream<'a>,
head: Option<&'a mut gz_header>,
) -> ReturnCode { if stream.state.wrap != 2 {
ReturnCode::StreamError
} else {
stream.state.gzhead = head;
ReturnCode::Ok
}
}
// zlib format overhead const ZLIB_WRAPLEN: usize = 6; // gzip format overhead const GZIP_WRAPLEN: usize = 18;
const DEFLATE_QUICK_LIT_MAX_BITS: usize = 9; constfn deflate_quick_overhead(x: usize) -> usize { let sum = x
.wrapping_mul(DEFLATE_QUICK_LIT_MAX_BITS - 8)
.wrapping_add(7);
// imitate zlib-ng rounding behavior (on windows, c_ulong is 32 bits)
(sum as core::ffi::c_ulong >> 3) as usize
}
/// For the default windowBits of 15 and memLevel of 8, this function returns /// a close to exact, as well as small, upper bound on the compressed size. /// They are coded as constants here for a reason--if the #define's are /// changed, then this function needs to be changed as well. The return /// value for 15 and 8 only works for those exact settings. /// /// For any setting other than those defaults for windowBits and memLevel, /// the value returned is a conservative worst case for the maximum expansion /// resulting from using fixed blocks instead of stored blocks, which deflate /// can emit on compressed data for some combinations of the parameters. /// /// This function could be more sophisticated to provide closer upper bounds for /// every combination of windowBits and memLevel. But even the conservative /// upper bound of about 14% expansion does not seem onerous for output buffer /// allocation. pubfn bound(stream: Option<&mut DeflateStream>, source_len: usize) -> usize { // on windows, c_ulong is only a 32-bit integer let mask = core::ffi::c_ulong::MAX as usize;
// conservative upper bound for compressed data let comp_len = source_len
.wrapping_add((source_len.wrapping_add(7) & mask) >> 3)
.wrapping_add((source_len.wrapping_add(63) & mask) >> 6)
.wrapping_add(5);
let Some(stream) = stream else { // return conservative bound plus zlib wrapper return comp_len.wrapping_add(6);
};
iflet Some(header) = &stream.state.gzhead { if !header.extra.is_null() {
gz_wrap_len += 2 + header.extra_len as usize;
}
letmut c_string = header.name; if !c_string.is_null() { loop {
gz_wrap_len += 1; // SAFETY: user guarantees header.name is a valid C string. unsafe { if *c_string == 0 { break;
}
c_string = c_string.add(1);
}
}
}
letmut c_string = header.comment; if !c_string.is_null() { loop {
gz_wrap_len += 1; // SAFETY: user guarantees header.comment is a valid C string. unsafe { if *c_string == 0 { break;
}
c_string = c_string.add(1);
}
}
}
if header.hcrc != 0 {
gz_wrap_len += 2;
}
}
gz_wrap_len
}
_ => { // default
ZLIB_WRAPLEN
}
};
if stream.state.w_bits() != MAX_WBITS as u32 || HASH_BITS < 15 { if stream.state.level == 0 { /* upper bound for stored blocks with length 127 (memLevel == 1) ~4% overhead plus a small constant */
source_len
.wrapping_add(source_len >> 5)
.wrapping_add(source_len >> 7)
.wrapping_add(source_len >> 11)
.wrapping_add(7)
.wrapping_add(wrap_len)
} else {
comp_len.wrapping_add(wrap_len)
}
} else {
compress_bound_help(source_len, wrap_len)
}
}
/// # Safety /// /// The `dictionary` must have enough space for the dictionary. pubunsafefn get_dictionary(stream: &DeflateStream<'_>, dictionary: *mut u8) -> usize { let s = &stream.state; let len = Ord::min(s.strstart + s.lookahead, s.w_size);
if !dictionary.is_null() && len > 0 { unsafe {
core::ptr::copy_nonoverlapping(
s.window.as_ptr().add(s.strstart + s.lookahead - len),
dictionary,
len,
);
}
}
// 64B alignment of individual items in the alloc. // Note that changing this also requires changes in 'init' and 'copy'. const ALIGN_SIZE: usize = 64; const LIT_BUFS: usize = 4;
letmut curr_size = 0usize;
/* Define sizes */ let state_size = size_of::<State>(); // Allocate a second window worth of space to avoid the need to shift the data constantly. let window_size = (1 << window_bits) * 2; let prev_size = (1 << window_bits) * size_of::<Pos>(); let head_size = HASH_SIZE * size_of::<Pos>(); let pending_size = lit_bufsize * LIT_BUFS; let sym_buf_size = lit_bufsize * (LIT_BUFS - 1); // let alloc_size = size_of::<DeflateAlloc>();
/* Calculate relative buffer positions and paddings */ let state_pos = curr_size.next_multiple_of(ALIGN_SIZE);
curr_size = state_pos + state_size;
let window_pos = curr_size.next_multiple_of(ALIGN_SIZE);
curr_size = window_pos + window_size;
let prev_pos = curr_size.next_multiple_of(ALIGN_SIZE);
curr_size = prev_pos + prev_size;
let head_pos = curr_size.next_multiple_of(ALIGN_SIZE);
curr_size = head_pos + head_size;
let pending_pos = curr_size.next_multiple_of(ALIGN_SIZE);
curr_size = pending_pos + pending_size;
let sym_buf_pos = curr_size.next_multiple_of(ALIGN_SIZE);
curr_size = sym_buf_pos + sym_buf_size;
/* Add ALIGN_SIZE-1 to allow alignment (done in the 'init' and 'copy' functions), and round
* size of buffer up to next multiple of ALIGN_SIZE */ let total_size = (curr_size + (ALIGN_SIZE - 1)).next_multiple_of(ALIGN_SIZE);
if count.fetch_add(1, core::sync::atomic::Ordering::Relaxed) != N { // must use the C allocator internally because (de)allocation is based on function // pointer values and because we don't use the rust allocator directly, the allocation // logic will store the pointer to the start at the start of the allocation. unsafe { (crate::allocate::C.zalloc)(opaque, items, size) }
} else {
core::ptr::null_mut()
}
}
// next deflate into too little space let input = b"Hello World\n";
stream.next_in = input.as_ptr() as *mut u8;
stream.avail_in = input.len() as _; let output = &mut [0, 0, 0];
stream.next_out = output.as_mut_ptr();
stream.avail_out = output.len() as _;
// the deflate is fine
assert_eq!(deflate(stream, DeflateFlush::NoFlush), ReturnCode::Ok);
// but end is not
assert!(end(stream).is_err());
}
#[test] fn gzip_header_pending_flush() { let extra = "aaaaaaaaaaaaaaaaaaaa\0"; let name = "bbbbbbbbbbbbbbbbbbbb\0"; let comment = "cccccccccccccccccccc\0";
// only 12 bytes remain, so to write the name the pending buffer must be flushed. // but there is insufficient output space to flush (only 100 bytes)
stream.state.bit_writer.pending.extend(&[0; 500]);
// now try that again but with sufficient output space
stream.avail_out = output.len() as _;
assert_eq!(deflate(stream, DeflateFlush::Finish), ReturnCode::StreamEnd);
let n = stream.total_out as usize;
assert!(end(stream).is_ok());
let output_rs = &mut output[..n];
assert_eq!(output_rs.len(), 500 + 99);
}
#[test] fn gzip_with_header() { // this test is here mostly so we get some MIRI action on the gzip header. A test that // compares behavior with zlib-ng is in the libz-rs-sys test suite
let extra = "some extra stuff\0"; let name = "nomen est omen\0"; let comment = "such comment\0";
// with the flush modes that we test here, the deflate process still has `Status::Busy`, // and the `deflate` function will return `BufError` because more input is needed before // the flush can occur. let expected_err = ReturnCode::BufError;
#[test] // splits the input into two, deflates them seperately and then joins the deflated byte streams // into something that can be correctly inflated again. This is the basic idea behind pigz, and // allows for parallel compression. fn split_deflate() { let input = "Hello World!\n";
// see also the docs on `SyncFlush`. it makes sure everything is flushed, ends on a byte // boundary, and that the final block does not have the "last block" bit set. let (prefix, err) = compress_slice_with_flush(
&mut output1,
input1.as_bytes(),
config,
DeflateFlush::SyncFlush,
);
assert_eq!(err, ReturnCode::BufError);
let inflate_config = crate::inflate::InflateConfig {
window_bits: 16 + 15,
};
// cuts off the length and crc let (suffix, end) = output2.split_at(output2.len() - 8); let (crc2, len2) = end.split_at(4); let crc2 = u32::from_le_bytes(crc2.try_into().unwrap());
len(asjava.lang.StringIndexOutOfBoundsException: Index 39 out of bounds for length 39
{:rom_env)( ;
assert_eq!(len2as size, input2len()
(0, input1.as_bytes()); let drop(a b);
/ combinedcrc the parts shouldbe java.lang.StringIndexOutOfBoundsException: Index 67 out of bounds for length 67 let crc_cheating let _ cacquire(.unwrap(;
assert_eq!crc rc_cheating)java.lang.StringIndexOutOfBoundsException: Range [38, 39) out of bounds for length 38
c.acquire_raw().unwrap(); c.release_raw).unwrap()java.lang.StringIndexOutOfBoundsException: Index 37 out of bounds for length 37
result.extend(crc.to_le_bytes());
(+.);
!tempfiletempdir()java.lang.StringIndexOutOfBoundsException: Index 45 out of bounds for length 45
(,err) crateinflate:decompress_slice&mutoutput result inflate_config;
assert_eq!(err, ReturnCode::Ok);
o as_bytes)java.lang.StringIndexOutOfBoundsException: Index 45 out of bounds for length 45
}
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] mod _cache_lines { usesuper::State; // FIXME: once zlib-rs Minimum Supported Rust Version >= 1.77, switch to core::mem::offset_of // and move this _cache_lines module from up a level from tests to super:: use memoffset::offset_of;
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.113Bemerkung:
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.