/// Initialize the stream in an inflate state pubfn back_init(stream: &mut z_stream, config: InflateConfig, window: Window) -> ReturnCode {
assert_eq!(1 << config.window_bits, window.buffer_size());
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` configures 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;
}
letmut state = State::new(&[], Writer::new(&mut []));
// TODO this can change depending on the used/supported SIMD instructions
state.chunksize = 32;
let alloc = Allocator {
zalloc: stream.zalloc.unwrap(),
zfree: stream.zfree.unwrap(),
opaque: stream.opaque,
_marker: PhantomData,
}; let allocs = InflateAllocOffsets::new();
let Some(allocation_start) = alloc.allocate_slice_raw::<u8>(allocs.total_size) else { return ReturnCode::MemError;
};
let address = allocation_start.as_ptr() as usize; let align_offset = address.next_multiple_of(64) - address; let buf = unsafe { allocation_start.as_ptr().add(align_offset) };
// NOTE: the window part of the allocation is ignored in this case.
state.window = window;
// SAFETY: we've correctly initialized the stream to be an InflateStream let Some(stream) = (unsafe { InflateStream::from_stream_mut(stream) }) else { return ReturnCode::StreamError;
};
dropbits!(2);
state.mode = Mode::Bad;
state.bad("invalid block type\0"); continue;
}
_ => { // LLVM will optimize this branch away
unreachable!("BitReader::bits(2) only yields a value of two bits, so this match is already exhaustive")
}
}
}
Mode::Stored => {
bytebits!();
needbits!(32);
if hold as u16 != !((hold >> 16) as u16) {
state.mode = Mode::Bad;
state.bad("invalid stored block lengths\0"); continue;
}
state.length = hold as usize & 0xFFFF;
tracev!("inflate: stored length {}", state.length);
initbits!();
/* copy stored block from input to output */ while state.length != 0 { letmut copy = state.length;
pull!();
room!();
copy = Ord::min(copy, have as usize);
copy = Ord::min(copy, left);
unsafe { core::ptr::copy(next, put, copy) };
have -= copy as u32;
next = unsafe { next.add(copy) };
while state.have < state.nlen + state.ndist { let here = loop { let here = state.len_table_get(bits!(state.len_table.bits) as usize); if here.bits <= bits { break here;
}
hold = state.bit_reader.hold();
bits = state.bit_reader.bits_in_buffer();
next = state.bit_reader.as_ptr();
have = state.bit_reader.bytes_remaining() as u32;
put = state.writer.next_out().cast();
left = state.writer.remaining();
continue'inf_leave;
}
let len_table = match state.len_table.codes {
Codes::Fixed => &crate::inflate::inffixed_tbl::LENFIX[..],
Codes::Codes => &state.codes_codes,
Codes::Len => &state.len_codes,
Codes::Dist => &state.dist_codes,
};
// get a literal, length, or end-of-block code letmut here; loop {
here = len_table[bits!(state.len_table.bits) as usize];
if here.bits <= bits { break;
}
pullbyte!();
}
if here.op != 0 && here.op & 0xf0 == 0 { let last = here; loop { let tmp = bits!((last.bits + last.op) as usize) as u16;
here = len_table[(last.val + (tmp >> last.bits)) as usize]; if last.bits + here.bits <= bits { break;
}
pullbyte!();
}
dropbits!(last.bits);
}
dropbits!(here.bits);
state.length = here.val as usize;
if here.op == 0 { if here.val >= 0x20 && here.val < 0x7f {
tracev!("inflate: literal '{}'", here.val as u8 as char);
} else {
tracev!("inflate: literal {:#04x}", here.val);
}
room!();
unsafe {
*put = state.length as u8;
put = put.add(1)
}
left -= 1;
state.mode = Mode::Len; continue;
} elseif here.op & 32 != 0 { // end of block
// get extra bits, if any if state.extra != 0 {
needbits!(state.extra);
state.length += bits!(state.extra) as usize;
dropbits!(state.extra as u8);
}
tracev!("inflate: length {}", state.length);
// get distance code letmut here; loop {
here = state.dist_table_get(bits!(state.dist_table.bits) as usize); if here.bits <= bits { break;
}
pullbyte!();
}
if here.op & 0xf0 == 0 { let last = here;
loop {
here = state.dist_table_get(
last.val as usize
+ ((bits!((last.bits + last.op) as usize) as usize) >> last.bits),
);
if copy < left {
from = put.wrapping_add(copy);
copy = left - copy;
} else {
from = put.wrapping_sub(state.offset);
copy = left;
}
copy = Ord::min(copy, state.length);
state.length -= copy;
left -= copy;
for _ in0..copy { unsafe {
*put = *from;
put = put.add(1);
from = from.add(1);
}
}
if state.length == 0 { break;
}
}
continue'inf_leave;
}
Mode::Done => {
ret = ReturnCode::StreamEnd; break'inf_leave;
}
Mode::Bad => {
ret = ReturnCode::DataError; break'inf_leave;
}
Mode::Head
| Mode::Flags
| Mode::Time
| Mode::Os
| Mode::ExLen
| Mode::Extra
| Mode::Name
| Mode::Comment
| Mode::HCrc
| Mode::Sync
| Mode::Mem
| Mode::Length
| Mode::TypeDo
| Mode::CopyBlock
| Mode::Check
| Mode::Len_
| Mode::Lit
| Mode::LenExt
| Mode::Dist
| Mode::DistExt
| Mode::Match
| Mode::LenLens
| Mode::CodeLens
| Mode::DictId
| Mode::Dict => { // All other states should be unreachable, and return StreamError.
ret = ReturnCode::StreamError; break'inf_leave;
}
}
}
if left < state.window.buffer_size()
&& unsafe {
out(
out_desc,
state.window.as_ptr().cast_mut(),
state.window.buffer_size() as u32 - left as u32,
)
} != 0
&& ret == ReturnCode::StreamEnd
{
ret = ReturnCode::BufError;
}
let lcode = state.len_table_ref(); let dcode = state.dist_table_ref();
// IDEA: use const generics for the bits here? let lmask = (1u64 << state.len_table.bits) - 1; let dmask = (1u64 << state.dist_table.bits) - 1;
// TODO verify if this is relevant for us let extra_safe = false;
let window_size = state.window.buffer_size();
letmut bad = None;
if bit_reader.bits_in_buffer() < 10 {
debug_assert!(bit_reader.bytes_remaining() >= 15); // Safety: Caller ensured that bit_reader has >= 15 bytes available; refill only needs 8. unsafe { bit_reader.refill() };
} // We had at least 15 bytes in the slice, plus whatever was in the buffer. After filling the // buffer from the slice, we now have at least 8 bytes remaining in the slice, plus a full buffer.
debug_assert!(
bit_reader.bytes_remaining() >= 8 && bit_reader.bytes_remaining_including_buffer() >= 15
);
'outer: loop { // This condition is ensured above for the first iteration of the `outer` loop. For // subsequent iterations, the loop continuation condition is // `bit_reader.bytes_remaining_including_buffer() > 15`. And because the buffer // contributes at most 7 bytes to the result of bit_reader.bytes_remaining_including_buffer(), // that means that the slice contains at least 8 bytes.
debug_assert!(
bit_reader.bytes_remaining() >= 8
&& bit_reader.bytes_remaining_including_buffer() >= 15
);
letmut here = { let bits = bit_reader.bits_in_buffer(); let hold = bit_reader.hold();
// Safety: As described in the comments for the debug_assert at the start of // the `outer` loop, it is guaranteed that `bit_reader.bytes_remaining() >= 8` here, // which satisfies the safety precondition for `refill`. And, because the total // number of bytes in `bit_reader`'s buffer plus its slice is at least 15, and // `refill` moves at most 7 bytes from the slice to the buffer, the slice will still // contain at least 8 bytes after this `refill` call. unsafe { bit_reader.refill() }; // After the refill, there will be at least 8 bytes left in the bit_reader's slice.
debug_assert!(bit_reader.bytes_remaining() >= 8);
// in most cases, the read can be interleaved with the logic // based on benchmarks this matters in practice. wild. if bits as usize >= state.len_table.bits {
lcode[(hold & lmask) as usize]
} else {
lcode[(bit_reader.hold() & lmask) as usize]
}
};
if here.op == 0 {
writer.push(here.val as u8);
bit_reader.drop_bits(here.bits);
here = lcode[(bit_reader.hold() & lmask) as usize];
if here.op == 0 {
writer.push(here.val as u8);
bit_reader.drop_bits(here.bits);
here = lcode[(bit_reader.hold() & lmask) as usize];
}
}
'dolen: loop {
bit_reader.drop_bits(here.bits); let op = here.op;
if op == 0 {
writer.push(here.val as u8);
} elseif op & 16 != 0 { let op = op & MAX_BITS; letmut len = here.val + bit_reader.bits(op as usize) as u16;
bit_reader.drop_bits(op);
here = dcode[(bit_reader.hold() & dmask) as usize];
// we have two fast-path loads: 10+10 + 15+5 = 40, // but we may need to refill here in the worst case if bit_reader.bits_in_buffer() < MAX_BITS + MAX_DIST_EXTRA_BITS {
debug_assert!(bit_reader.bytes_remaining() >= 8); // Safety: On the first iteration of the `dolen` loop, we can rely on the // invariant documented for the previous `refill` call above: after that // operation, `bit_reader.bytes_remining >= 8`, which satisfies the safety // precondition for this call. For subsequent iterations, this invariant // remains true because nothing else within the `dolen` loop consumes data // from the slice. unsafe { bit_reader.refill() };
}
'dodist: loop {
bit_reader.drop_bits(here.bits); let op = here.op;
if op & 16 != 0 { let op = op & MAX_BITS; let dist = here.val + bit_reader.bits(op as usize) as u16;
if INFLATE_STRICT && dist as usize > state.dmax {
bad = Some("invalid distance too far back\0");
state.mode = Mode::Bad; break'outer;
}
bit_reader.drop_bits(op);
// max distance in output let written = writer.len();
if dist as usize > written { // copy fropm the window if (dist as usize - written) > state.window.have() { if state.flags.contains(Flags::SANE) {
bad = Some("invalid distance too far back\0");
state.mode = Mode::Bad; break'outer;
}
if window_next == 0 { // This case is hit when the window has just wrapped around // by logic in `Window::extend`. It is special-cased because // apparently this is quite common. // // the match is at the end of the window, even though the next // position has now wrapped around.
from = window_size - op;
} elseif window_next >= op { // the standard case: a contiguous copy from the window, no wrapping
from = window_next - op;
} else { // This case is hit when the window has recently wrapped around // by logic in `Window::extend`. // // The match is (partially) at the end of the window
op -= window_next;
from = window_size - op;
if op < len as usize { // This case is hit when part of the match is at the end of the // window, and part of it has wrapped around to the start. Copy // the end section here, the start section will be copied below.
len -= op as u16;
writer.extend_from_window_back(&state.window, from..from + op);
from = 0;
op = window_next;
}
}
let copy = Ord::min(op, len as usize);
writer.extend_from_window_back(&state.window, from..from + copy);
if op < len as usize { // here we need some bytes from the output itself
writer.copy_match_back(dist as usize, len as usize - op);
}
} elseif extra_safe {
todo!()
} else {
writer.copy_match_back(dist as usize, len as usize)
}
} elseif (op & 64) == 0 { // 2nd level distance code
here = dcode[(here.val + bit_reader.bits(op as usize) as u16) as usize]; continue'dodist;
} else {
bad = Some("invalid distance code\0");
state.mode = Mode::Bad; break'outer;
}
break'dodist;
}
} elseif (op & 64) == 0 { // 2nd level length code
here = lcode[(here.val + bit_reader.bits(op as usize) as u16) as usize]; continue'dolen;
} elseif op & 32 != 0 { // end of block
state.mode = Mode::Type; break'outer;
} else {
bad = Some("invalid literal/length code\0");
state.mode = Mode::Bad; break'outer;
}
break'dolen;
}
// For normal `inflate`, include the bits in the bit_reader buffer in the count of available bytes. let remaining = bit_reader.bytes_remaining(); if remaining >= INFLATE_FAST_MIN_HAVE && writer.remaining() >= INFLATE_FAST_MIN_LEFT { continue;
}
break'outer;
}
// return unused bytes (on entry, bits < 8, so in won't go too far back)
bit_reader.return_unused_bytes();
iflet Some(error_message) = bad {
debug_assert!(matches!(state.mode, Mode::Bad));
state.bad(error_message);
}
}
pubfn back_end<'a>(strm: &'a mut InflateStream<'a>) { // With infback the window is user-supplied, so we mustn't try to free it. let _ = core::mem::replace(&mut strm.state.window, Window::empty()); crate::inflate::end(strm);
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.51 Sekunden
(vorverarbeitet am 2026-08-25)
¤
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.