/// # 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: InflateStream has an equivalent layout as z_stream
strm.cast::<InflateStream>().as_ref()
}
/// # 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: InflateStream has an equivalent layout as z_stream
strm.cast::<InflateStream>().as_mut()
}
fn as_z_stream_mut(&mutself) -> &mut z_stream { // safety: a valid &mut InflateStream is also a valid &mut z_stream unsafe { &mut *(selfas *mut _ as *mut z_stream) }
}
}
const MAX_BITS: u8 = 15; // maximum number of bits in a code const MAX_DIST_EXTRA_BITS: u8 = 13; // maximum number of extra distance bits // pubfn uncompress_slice<'a>(
output: &'a mut [u8],
input: &[u8],
config: InflateConfig,
) -> (&'a mut [u8], ReturnCode) { let output_uninit = unsafe {
core::slice::from_raw_parts_mut(output.as_mut_ptr() as *mut MaybeUninit<u8>, output.len())
};
uncompress(output_uninit, input, config)
}
/// Inflates `source` into `dest`, and writes the final inflated size into `dest_len`. pubfn uncompress<'a>(
output: &'a mut [MaybeUninit<u8>],
input: &[u8],
config: InflateConfig,
) -> (&'a mut [u8], ReturnCode) { letmut dest_len_ptr = output.len() as z_checksum;
// for detection of incomplete stream when *destLen == 0 letmut buf = [0u8];
letmut left; letmut len = input.len() as u64;
let dest = if output.is_empty() {
left = 1;
buf.as_mut_ptr()
} else {
left = output.len() as u64;
dest_len_ptr = 0;
let err = loop { if stream.avail_out == 0 {
stream.avail_out = Ord::min(left, u32::MAX as u64) as u32;
left -= stream.avail_out as u64;
}
if stream.avail_in == 0 {
stream.avail_in = Ord::min(len, u32::MAX as u64) as u32;
len -= stream.avail_in as u64;
}
let err = unsafe { inflate(stream, InflateFlush::NoFlush) };
if err != ReturnCode::Ok { break err;
}
};
if !output.is_empty() {
dest_len_ptr = stream.total_out;
} elseif stream.total_out != 0 && err == ReturnCode::BufError {
left = 1;
}
let avail_out = stream.avail_out;
end(stream);
let ret = match err {
ReturnCode::StreamEnd => ReturnCode::Ok,
ReturnCode::NeedDict => ReturnCode::DataError,
ReturnCode::BufError if (left + avail_out as u64) != 0 => ReturnCode::DataError,
_ => err,
};
// SAFETY: we have now initialized these bytes let output_slice = unsafe {
core::slice::from_raw_parts_mut(output.as_mut_ptr() as *mut u8, dest_len_ptr as usize)
};
pub(crate) struct State<'a> { /// Current inflate mode
mode: Mode,
/// true if processing the last block
last: bool, /// bitflag /// /// - bit 0 true if zlib /// - bit 1 true if gzip /// - bit 2 true to validate check value
wrap: usize,
/// table for length/literal codes
len_table: Table,
/// table for dist codes
dist_table: Table,
/// log base 2 of requested window size
wbits: usize, // allocated window if needed (capacity == 0 if unused)
window: Window<'a>,
/// place to store gzip header if needed
head: Option<&'a mut gz_header>,
// /// number of code length code lengths
ncode: usize, /// number of length code lengths
nlen: usize, /// number of distance code lengths
ndist: usize, /// number of code lengths in lens[]
have: usize, /// next available space in codes[]
next: usize, // represented as an index, don't want a self-referential structure here
// IO
bit_reader: BitReader<'a>,
writer: ReadBuf<'a>,
total: usize,
/// length of a block to copy
length: usize, /// distance back to copy the string from
offset: usize,
/// extra bits needed
extra: usize,
/// if false, allow invalid distance too far
sane: bool, /// bits back of last unprocessed length/lit
back: usize,
/// initial length of match
was: usize,
/// size of memory copying chunk
chunksize: usize,
in_available: usize,
out_available: usize,
/// temporary storage space for code lengths
lens: [u16; 320], /// work area for code table building
work: [u16; 288],
let b0 = self.bit_reader.bits(8) as u8; let b1 = (self.bit_reader.hold() >> 8) as u8; self.checksum = crc32(crate::CRC32_INITIAL_VALUE, &[b0, b1]); self.bit_reader.init_bits();
// self.length (and head.extra_len) represent the length of the extra field self.length = self.bit_reader.hold() as usize; iflet Some(head) = self.head.as_mut() {
head.extra_len = self.length as u32;
}
fn extra(&mutself) -> ReturnCode { if (self.flags & 0x0400) != 0 { // self.length is the number of remaining `extra` bytes. But they may not all be available let extra_available = Ord::min(self.length, self.bit_reader.bytes_remaining()); let extra_slice = &self.bit_reader.as_slice()[..extra_available];
if !extra_slice.is_empty() { iflet Some(head) = self.head.as_mut() { if !head.extra.is_null() { let written_so_far = head.extra_len as usize - self.length;
let count = Ord::min(
(head.extra_max as usize).saturating_sub(written_so_far),
extra_slice.len(),
);
// the name string will always be null-terminated, but might be longer than we have // space for in the header struct. Nonetheless, we read the whole thing. let slice = self.bit_reader.as_slice(); let null_terminator_index = slice.iter().position(|c| *c == 0);
// we include the null terminator if it exists let name_slice = match null_terminator_index {
Some(i) => &slice[..=i],
None => slice,
};
// if the header has space, store as much as possible in there iflet Some(head) = self.head.as_mut() { if !head.name.is_null() { let remaining_name_bytes = (head.name_max as usize).saturating_sub(self.length); let copy = Ord::min(name_slice.len(), remaining_name_bytes);
// the comment string will always be null-terminated, but might be longer than we have // space for in the header struct. Nonetheless, we read the whole thing. let slice = self.bit_reader.as_slice(); let null_terminator_index = slice.iter().position(|c| *c == 0);
// we include the null terminator if it exists let comment_slice = match null_terminator_index {
Some(i) => &slice[..=i],
None => slice,
};
// if the header has space, store as much as possible in there iflet Some(head) = self.head.as_mut() { if !head.comment.is_null() { let remaining_comm_bytes = (head.comm_max as usize).saturating_sub(self.length); let copy = Ord::min(comment_slice.len(), remaining_comm_bytes); unsafe {
core::ptr::copy_nonoverlapping(
comment_slice.as_ptr(),
head.comment.add(self.length),
copy,
)
};
fn len(&mutself) -> ReturnCode { let avail_in = self.bit_reader.bytes_remaining(); let avail_out = self.writer.remaining();
// INFLATE_FAST_MIN_LEFT is important. It makes sure there is at least 32 bytes of free // space available. This means for many SIMD operations we don't need to process a // remainder; we just copy blindly, and a later operation will overwrite the extra copied // bytes if avail_in >= INFLATE_FAST_MIN_HAVE && avail_out >= INFLATE_FAST_MIN_LEFT { return inflate_fast_help(self, 0);
}
self.back = 0;
// get a literal, length, or end-of-block code letmut here; loop { let bits = self.bit_reader.bits(self.len_table.bits);
here = self.len_table_get(bits as usize);
if here.bits <= self.bit_reader.bits_in_buffer() { break;
}
pull_byte!(self);
}
if here.op != 0 && here.op & 0xf0 == 0 { let last = here; loop { let bits = self.bit_reader.bits((last.bits + last.op) as usize) as u16;
here = self.len_table_get((last.val + (bits >> last.bits)) as usize); if last.bits + here.bits <= self.bit_reader.bits_in_buffer() { break;
}
pull_byte!(self);
}
self.bit_reader.drop_bits(last.bits as usize); self.back += last.bits as usize;
}
self.bit_reader.drop_bits(here.bits as usize); self.back += here.bits as usize; self.length = here.val as usize;
if here.op == 0 { self.mode = Mode::Lit; self.lit()
} elseif here.op & 32 != 0 { // end of block
fn len_ext(&mutself) -> ReturnCode { let extra = self.extra;
// get extra bits, if any if extra != 0 {
need_bits!(self, extra); self.length += self.bit_reader.bits(extra) as usize; self.bit_reader.drop_bits(extra); self.back += extra;
}
fn dist(&mutself) -> ReturnCode { // get distance code letmut here; loop { let bits = self.bit_reader.bits(self.dist_table.bits) as usize;
here = self.dist_table_get(bits); if here.bits <= self.bit_reader.bits_in_buffer() { break;
}
pull_byte!(self);
}
if here.op & 0xf0 == 0 { let last = here;
loop { let bits = self.bit_reader.bits((last.bits + last.op) as usize);
here = self.dist_table_get(last.val as usize + ((bits as usize) >> last.bits));
if last.bits + here.bits <= self.bit_reader.bits_in_buffer() { break;
}
pull_byte!(self);
}
self.bit_reader.drop_bits(last.bits as usize); self.back += last.bits as usize;
}
fn code_lens(&mutself) -> ReturnCode { whileself.have < self.nlen + self.ndist { let here = loop { let bits = self.bit_reader.bits(self.len_table.bits); let here = self.len_table_get(bits as usize); if here.bits <= self.bit_reader.bits_in_buffer() { break here;
}
for _ in0..copy { self.lens[self.have] = len as u16; self.have += 1;
}
} 18.. => {
need_bits!(self, here_bits + 7); self.bit_reader.drop_bits(here_bits); let len = 0; let copy = 11 + self.bit_reader.bits(7) as usize; self.bit_reader.drop_bits(7);
// NOTE: it is crucial for the internal bookkeeping that this is the only route for actually // leaving the inflate function call chain fn inflate_leave(&mutself, return_code: ReturnCode) -> ReturnCode { // actual logic is in `inflate` itself
return_code
}
/// Stored in the `z_stream.data_type` field fn decoding_state(&self) -> i32 { let bit_reader_bits = self.bit_reader.bits_in_buffer() as i32;
debug_assert!(bit_reader_bits < 64);
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.size();
letmut bad = None;
if bit_reader.bits_in_buffer() < 10 {
bit_reader.refill();
}
'outer: loop { letmut here = bit_reader.refill_and(|hold| lcode[(hold & lmask) as usize]);
if here.op == 0 {
writer.push(here.val as u8);
bit_reader.drop_bits(here.bits as usize);
here = lcode[(bit_reader.hold() & lmask) as usize];
if here.op == 0 {
writer.push(here.val as u8);
bit_reader.drop_bits(here.bits as usize);
here = lcode[(bit_reader.hold() & lmask) as usize];
}
}
'dolen: loop {
bit_reader.drop_bits(here.bits as usize); 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 as usize);
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 {
bit_reader.refill();
}
'dodist: loop {
bit_reader.drop_bits(here.bits as usize); 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 dist as usize > state.dmax {
bad = Some("invalid distance too far back\0");
state.mode = Mode::Bad; break'outer;
}
bit_reader.drop_bits(op as usize);
// 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.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(&state.window.as_slice()[from..][..op]);
from = 0;
op = window_next;
}
}
let copy = Ord::min(op, len as usize);
writer.extend(&state.window.as_slice()[from..][..copy]);
if op < len as usize { // here we need some bytes from the output itself
writer.copy_match(dist as usize, len as usize - op);
}
} elseif extra_safe {
todo!()
} else {
writer.copy_match(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;
}
let remaining = bit_reader.bytes_remaining(); if remaining.saturating_sub(INFLATE_FAST_MIN_LEFT - 1) > 0
&& 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();
/// Initialize the stream in an inflate state pubfn init(stream: &mut z_stream, config: InflateConfig) -> ReturnCode {
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(&[], ReadBuf::new(&mut []));
// TODO this can change depending on the used/supported SIMD instructions
state.chunksize = 32;
// allocated here to have the same order as zlib let Some(state_allocation) = alloc.allocate::<State>() else { return ReturnCode::MemError;
};
stream.state = state_allocation.write(state) as *mut _ as *mut internal_state;
// SAFETY: we've correctly initialized the stream to be an InflateStream let ret = iflet Some(stream) = unsafe { InflateStream::from_stream_mut(stream) } {
reset_with_config(stream, config)
} else {
ReturnCode::StreamError
};
if ret != ReturnCode::Ok { let ptr = stream.state;
stream.state = core::ptr::null_mut(); // SAFETY: we assume deallocation does not cause UB unsafe { alloc.deallocate(ptr, 1) };
}
let source_slice = core::slice::from_raw_parts(stream.next_in, stream.avail_in as usize); let dest_slice = core::slice::from_raw_parts_mut(stream.next_out, stream.avail_out asusize);
state.in_available = stream.avail_in as _;
state.out_available = stream.avail_out as _;
letmut err = state.dispatch();
let in_read = state.bit_reader.as_ptr() as usize - stream.next_in as usize; let out_written = state.out_available - (state.writer.capacity() - state.writer.len());
stream.total_in += in_read as z_size;
state.total += out_written;
stream.total_out = state.total as _;
stream.avail_in = state.bit_reader.bytes_remaining() as u32;
stream.next_in = state.bit_reader.as_ptr() as *mut u8;
stream.avail_out = (state.writer.capacity() - state.writer.len()) as u32;
stream.next_out = state.writer.next_out() as *mut u8;
iflet Some(msg) = state.error_message {
assert!(msg.ends_with(|c| c == '\0'));
stream.msg = msg.as_ptr() as *mut u8 as *mut core::ffi::c_char;
}
stream.data_type = state.decoding_state();
if ((in_read == 0 && out_written == 0) || flush == InflateFlush::Finish as _)
&& err == (ReturnCode::Ok as _)
{
ReturnCode::BufError as _
} else {
err as _
}
}
fn syncsearch(mut got: usize, buf: &[u8]) -> (usize, usize) { let len = buf.len(); letmut next = 0;
while next < len && got < 4 { if buf[next] == if got < 2 { 0 } else { 0xff } {
got += 1;
} elseif buf[next] != 0 {
got = 0;
} else {
got = 4 - got;
}
next += 1;
}
(got, next)
}
pubfn sync(stream: &mut InflateStream) -> ReturnCode { let state = &mut stream.state;
if stream.avail_in == 0 && state.bit_reader.bits_in_buffer() < 8 { return ReturnCode::BufError;
} /* if first time, start search in bit buffer */ if !matches!(state.mode, Mode::Sync) {
state.mode = Mode::Sync;
let (buf, len) = state.bit_reader.start_sync_search();
(state.have, _) = syncsearch(0, &buf[..len]);
}
// search available input let slice = unsafe { core::slice::from_raw_parts(stream.next_in, stream.avail_in as usize) };
let len;
(state.have, len) = syncsearch(state.have, slice);
stream.next_in = unsafe { stream.next_in.add(len) };
stream.avail_in -= len as u32;
stream.total_in += len as z_size;
/* return no joy or set up to restart inflate() on a new block */ if state.have != 4 { return ReturnCode::DataError;
}
if state.flags == -1 {
state.wrap = 0; /* if no header yet, treat as raw */
} else {
state.wrap &= !4; /* no point in computing a check value now */
}
let flags = state.flags; let total_in = stream.total_in; let total_out = stream.total_out;
// 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);
}
// allocated here to have the same order as zlib let Some(state_allocation) = source.alloc.allocate::<State>() else { return ReturnCode::MemError;
};
let state = &source.state;
let writer: MaybeUninit<ReadBuf> = unsafe { core::ptr::read(&state.writer as *const _ as *const MaybeUninit<ReadBuf>) };
if !state.window.is_empty() { let Some(window) = state.window.clone_in(&source.alloc) else {
source.alloc.deallocate(state_allocation.as_mut_ptr(), 1); return ReturnCode::MemError;
};
copy.window = window;
}
// write the cloned state into state_ptr let state_ptr = state_allocation.write(copy);
// 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 writer; it cannot be cloned so we need to use some shennanigans let field_ptr = unsafe { core::ptr::addr_of_mut!((*dest.as_mut_ptr()).state.writer) }; unsafe { core::ptr::copy(writer.as_ptr(), field_ptr, 1) };
// 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.head) }; unsafe { core::ptr::copy(&source.state.head, field_ptr, 1) };
// safety: window is not used again if !window.is_empty() { let window = window.into_inner(); unsafe { alloc.deallocate(window.as_mut_ptr(), window.len()) };
}
let stream = stream.as_z_stream_mut();
let state_ptr = core::mem::replace(&mut stream.state, core::ptr::null_mut());
// safety: state_ptr is not used again unsafe { alloc.deallocate(state_ptr as *mut State, 1) };
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.