/// # 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() }
}
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) }
}
// TODO: This could use `MaybeUninit::slice_assume_init` when it is stable. unsafefn slice_assume_init_mut<T>(slice: &mut [MaybeUninit<T>]) -> &pan style='color:red'>mut [T] {
&mut *(slice as *mut [MaybeUninit<T>] as *mut [T])
}
// when stable, use MaybeUninit::write_slice fn slice_to_uninit<T>(slice: &[T]) -> &[MaybeUninit<T>] { // safety: &[T] and &[MaybeUninit<T>] have the same layout unsafe { &*(slice as *const [T] as *const [MaybeUninit<T>]) }
}
/* 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;
// just provide a valid default; gets set properly later
hash_calc_variant: HashCalcVariant::Standard,
};
let state = state_allocation.write(state);
stream.state = state as *mut _ as *mut internal_state;
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;
};
// 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.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 { // 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 alloc = &source.alloc;
// allocated here to have the same order as zlib let Some(state_allocation) = alloc.allocate::<State>() else { return ReturnCode::MemError;
};
let source_state = &source.state;
let window = source_state.window.clone_in(alloc);
let prev = alloc.allocate_slice::<u16>(source_state.w_size); let head = alloc.allocate::<[u16; HASH_SIZE]>();
let pending = source_state.bit_writer.pending.clone_in(alloc); let sym_buf = source_state.sym_buf.clone_in(alloc);
// if any allocation failed, clean up allocations that did succeed let (window, prev, head, pending, sym_buf) = match (window, prev, head, pending, sym_buf) {
(Some(window), Some(prev), Some(head), Some(pending), Some(sym_buf)) => {
(window, prev, head, pending, sym_buf)
}
(window, prev, head, pending, sym_buf) => { // Safety: this access is in-bounds let field_ptr = unsafe { core::ptr::addr_of_mut!((*dest.as_mut_ptr()).state) }; unsafe { core::ptr::write(field_ptr as *mut *mut State, core::ptr::null_mut()) };
// Safety: it is an assumpion on DeflateStream that (de)allocation does not cause UB. unsafe { iflet Some(mut sym_buf) = sym_buf {
alloc.deallocate(sym_buf.as_mut_ptr(), sym_buf.capacity())
} iflet Some(pending) = pending {
pending.drop_in(alloc);
} iflet Some(head) = head {
alloc.deallocate(head.as_mut_ptr(), HASH_SIZE)
} iflet Some(prev) = prev {
alloc.deallocate(prev.as_mut_ptr(), prev.len())
} iflet Some(mut window) = window {
window.drop_in(alloc);
}
prev.copy_from_slice(slice_to_uninit(source_state.prev)); let prev = unsafe { core::slice::from_raw_parts_mut(prev.as_mut_ptr().cast(), prev.len()) }; let head = head.write(*source_state.head);
// write the cloned state into state_ptr let state_ptr = state_allocation.write(dest_state);
// 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_ptr) };
// 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 alloc = stream.alloc;
// deallocate in reverse order of allocations unsafe { // safety: we make sure that these fields are not used (by invalidating the state pointer)
stream.state.sym_buf.drop_in(&alloc);
stream.state.bit_writer.pending.drop_in(&alloc);
alloc.deallocate(stream.state.head, 1); if !stream.state.prev.is_empty() {
alloc.deallocate(stream.state.prev.as_mut_ptr(), stream.state.prev.len());
}
stream.state.window.drop_in(&alloc);
}
let state = stream.state as *mut State; let stream = stream.as_z_stream_mut();
stream.state = core::ptr::null_mut();
// safety: `state` is not used later unsafe {
alloc.deallocate(state, 1);
}
match status {
Status::Busy => Err(stream),
_ => Ok(stream),
}
}
pubfn reset(stream: &mut DeflateStream) -> ReturnCode { let ret = reset_keep(stream);
if ret == ReturnCode::Ok {
lm_init(stream.state);
}
fn lm_set_level(state: &mut State, level: i8) {
state.max_lazy_match = CONFIGURATION_TABLE[level as usize].max_lazy as usize;
state.good_match = CONFIGURATION_TABLE[level as usize].good_length as usize;
state.nice_match = CONFIGURATION_TABLE[level as usize].nice_length as usize;
state.max_chain_length = CONFIGURATION_TABLE[level as usize].max_chain as usize;
/// 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,
}
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);
}
self.send_bits_trace(val, len); self.sent_bits_add(len as usize);
if total_bits < Self::BIT_BUF_SIZE { self.bit_buffer |= val << self.bits_used; self.bits_used = total_bits;
} elseifself.bits_used == Self::BIT_BUF_SIZE { // with how send_bits is called, this is unreachable in practice self.pending.extend(&self.bit_buffer.to_le_bytes()); self.bit_buffer = val; self.bits_used = len;
} else { self.bit_buffer |= val << self.bits_used; self.pending.extend(&self.bit_buffer.to_le_bytes()); self.bit_buffer = val >> (Self::BIT_BUF_SIZE - self.bits_used); self.bits_used = total_bits - Self::BIT_BUF_SIZE;
}
}
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);
}
/* Send the length code, len is the match length - STD_MIN_MATCH */ letmut 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; letmut 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;
}
dist -= 1; /* dist is now the match distance - 1 */
code = State::d_code(dist) as usize;
assert!(code < D_CODES, "bad d_code"); // send_code_trace(s, code);
/* Send the distance code */ let dnode = dtree[code];
match_bits |= (dnode.code() as u64) << match_bits_len;
match_bits_len += dnode.len() as usize;
extra = StaticTreeDesc::EXTRA_DBITS[code] as usize; if extra != 0 {
dist -= self::trees_tbl::BASE_DIST[code] as usize;
match_bits |= (dist as u64) << match_bits_len;
match_bits_len += extra;
}
self.send_bits(match_bits, match_bits_len as u8);
match_bits_len
}
fn compress_block_help(&mutself, sym_buf: &[u8], ltree: &[Value], dtree: &[Value]) { for chunk in sym_buf.chunks_exact(3) { let [dist_low, dist_high, lc] = *chunk else {
unreachable!("out of bound access on the symbol buffer");
};
match u16::from_be_bytes([dist_high, dist_low]) as usize { 0 => self.emit_lit(ltree, lc) as usize,
dist => 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 */
/// Use a faster search when the previous match is longer than this pub(crate) good_match: usize,
/// Stop searching when current match exceeds this pub(crate) nice_match: usize,
// part of the fields below // dyn_ltree: [Value; ], // dyn_dtree: [Value; ], // bl_tree: [Value; ],
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 */
pub(crate) bl_count: [u16; MAX_BITS + 1],
pub(crate) match_length: usize, /* length of best match */ pub(crate) prev_match: u16, /* previous match */ pub(crate) match_available: bool, /* set if previous match exists */ pub(crate) strstart: usize, /* start of string to insert */ pub(crate) match_start: usize, /* start of matching string */
/// 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: usize,
/// 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: usize,
// 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 <= 3. // 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: usize,
/// Window position at the beginning of the current output block. Gets /// negative when the window is moved backwards. pub(crate) block_start: isize,
/// Whether or not a block is currently open for the QUICK deflation scheme. /// true if there is an active block, or false if the block was just closed pub(crate) block_open: u8,
pub(crate) window: Window<'a>,
pub(crate) sym_buf: ReadBuf<'a>,
/// 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*wSize, except when the user input buffer is directly used as sliding window. pub(crate) window_size: usize,
/// number of string matches in current block pub(crate) matches: usize,
/// 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,
pub(crate) w_size: usize, /* LZ77 window size (32K by default) */ pub(crate) w_bits: usize, /* log2(w_size) (8..16) */ pub(crate) w_mask: usize, /* w_size - 1 */ pub(crate) lookahead: usize, /* number of valid bytes ahead in window */
// 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
}
/// 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
}
#[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;
window.initialize_at_least(offset + len); 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;
window.initialize_at_least(offset + len); 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;
window.initialize_at_least(offset + len); unsafe { window.copy_and_initialize(offset..offset + len, stream.next_in) };
}
stream.next_in = stream.next_in.wrapping_add(len);
stream.total_in += 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() { // in some cases zlib-ng copies uninitialized bytes here. We cannot have that, so // explicitly initialize them with zeros. // // see also the "fill_window_out_of_bounds" test.
state.window.initialize_at_least(2 * wsize);
state.window.filled_mut().copy_within(wsize..2 * wsize, 0);
if state.match_start >= wsize {
state.match_start -= wsize;
} else {
state.match_start = 0;
state.prev_length = 0;
}
state.strstart -= wsize; /* we now have strstart >= MAX_DIST */
state.block_start -= wsize as isize; if state.insert > state.strstart {
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) as usize;
} 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.
// initialize some memory at the end of the (filled) window, so SIMD operations can go "out of // bounds" without violating any requirements. The window allocation is already slightly bigger // to allow for this.
stream.state.window.initialize_out_of_bounds();
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 state.heap.heap_len < 2 {
state.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;
state.heap.heap[state.heap.heap_len] = node as u32;
*tree[node].freq_mut() = 1;
state.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 = state.heap.heap_len / 2; while n >= 1 {
state.heap.pqdownheap(tree, n);
n -= 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;
state.bl_count.fill(0);
// 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;
}
state.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;
}
/* Find the first bit length which could increase: */ loop { letmut bits = max_length as usize - 1; while state.bl_count[bits] == 0 {
bits -= 1;
}
state.bl_count[bits] -= 1; /* move one leaf down the tree */
state.bl_count[bits + 1] += 2; /* move one overflow item as its brother */
state.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 = state.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;
}
}
}
/// 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;
}
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 i32;
/* 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 i32) <= 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::Name { iflet Some(gzhead) = stream.state.gzhead.as_ref() { if !gzhead.name.is_null() { 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() { 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.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);
// 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 let return_code = iflet Some(stream) = unsafe { DeflateStream::from_stream_mut(&e='color:red'>mut stream) } { match end(stream) {
Ok(_) => ReturnCode::Ok,
Err(_) => ReturnCode::DataError,
}
} else {
ReturnCode::Ok
};
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 smaller(tree: &[Value], n: u32, m: u32, depth: &[u8]) -> bool { let (n, m) = (n as usize, m as usize);
fn pqdownheap(&mutself, tree: &[Value], mut k: usize) { /* tree: the tree to restore */ /* k: node to move down */
let v = self.heap[k]; letmut j = k << 1; /* left son of k */
while j <= self.heap_len { /* Set j to the smallest of the two sons: */ if j < self.heap_len { let cond = Self::smaller(tree, self.heap[j + 1], self.heap[j], &self.depth); if cond {
j += 1;
}
}
/* Exit if v is smaller than both sons */ ifSelf::smaller(tree, v, self.heap[j], &self.depth) { 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;
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);
};
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.
(crate::allocate::Allocator::C.zalloc)(opaque, items, size)
} else {
core::ptr::null_mut()
}
}
#[test] fn init_invalid_allocator() {
{ let atomic = AtomicUsize::new(0); letmut stream = z_stream {
zalloc: Some(fail_nth_allocation::<0>),
zfree: Some(crate::allocate::Allocator::C.zfree),
opaque: &atomic as *const _ as *const core::ffi::c_void as *mut _,
..z_stream::default()
};
assert_eq!(
init(&mut stream, DeflateConfig::default()),
ReturnCode::MemError
);
}
{ let atomic = AtomicUsize::new(0); letmut stream = z_stream {
zalloc: Some(fail_nth_allocation::<3>),
zfree: Some(crate::allocate::Allocator::C.zfree),
opaque: &atomic as *const _ as *const core::ffi::c_void as *mut _,
..z_stream::default()
};
assert_eq!(
init(&mut stream, DeflateConfig::default()),
ReturnCode::MemError
);
}
{ let atomic = AtomicUsize::new(0); letmut stream = z_stream {
zalloc: Some(fail_nth_allocation::<5>),
zfree: Some(crate::allocate::Allocator::C.zfree),
opaque: &atomic as *const _ as *const core::ffi::c_void as *mut _,
..z_stream::default()
};
assert_eq!(
init(&mut stream, DeflateConfig::default()),
ReturnCode::MemError
);
}
}
// 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 `deflateEnd` function will return `DataError`. let expected_err = ReturnCode::DataError;
#[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::DataError);
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());
// cuts off the gzip header (10 bytes) from the front let suffix = &suffix[10..];
// it would be more proper to use `stream.total_in` here, but the slice helpers hide the // stream so we're cheating a bit here let len1 = input1.len() as u32; let len2 = u32::from_le_bytes(len2.try_into().unwrap());
assert_eq!(len2 as usize, input2.len());
let crc1 = crate::crc32(0, input1.as_bytes()); let crc = crate::crc32_combine(crc1, crc2, len2 as u64);
// combined crc of the parts should be the crc of the whole let crc_cheating = crate::crc32(0, input.as_bytes());
assert_eq!(crc, crc_cheating);
// write the trailer
result.extend(crc.to_le_bytes());
result.extend((len1 + len2).to_le_bytes());
// the output is slightly different based on what hashing algorithm is used match HashCalcVariant::for_compression_level(config.level as usize) {
HashCalcVariant::Crc32 => { // the aarch64 hashing algorithm is different from the standard algorithm, but in // this case they turn out to give the same output. Beware! if cfg!(target_arch = "x86") || cfg!(target_arch = "x86_64") {
fuzz_based_test(&input, config, &crc32);
} else {
fuzz_based_test(&input, config, &other);
}
}
HashCalcVariant::Standard | HashCalcVariant::Roll => {
fuzz_based_test(&input, config, &other);
}
}
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.84 Sekunden
(vorverarbeitet am 2026-06-18)
¤
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.