Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions zlib-rs/src/inflate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,10 @@ const fn zswap32(q: u32) -> u32 {
}

const INFLATE_FAST_MIN_HAVE: usize = 15;
const INFLATE_FAST_MIN_LEFT: usize = 260;
// 260 = up to 2 literals from the fast-path plus one max-length (258) match. The
// unchecked match copy overshoots by up to 32 bytes (one SIMD chunk), so the
// margin covers both, letting the fast loop skip the per-match bounds check.
const INFLATE_FAST_MIN_LEFT: usize = 260 + 32;

impl State<'_> {
// This logic is split into its own function for two reasons
Expand Down Expand Up @@ -2110,7 +2113,16 @@ unsafe fn inflate_fast_help_impl<const FEATURES: usize>(state: &mut State, _star
} else if extra_safe {
todo!()
} else {
writer.copy_match_with_features::<FEATURES>(dist as usize, len as usize)
// SAFETY: `dist <= written` (the `else` of the window-copy
// branch) gives `offset_from_end <= filled`, and
// INFLATE_FAST_MIN_LEFT guarantees at least `len + 32` bytes of
// spare capacity for this iteration's writes.
unsafe {
writer.copy_match_unchecked_with_features::<FEATURES>(
dist as usize,
len as usize,
)
}
}
} else if (op & 64) == 0 {
// 2nd level distance code
Expand Down
72 changes: 72 additions & 0 deletions zlib-rs/src/inflate/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,78 @@ impl<'a> Writer<'a> {
self.copy_match_help::<8>(offset_from_end, length)
}

/// Like [`copy_match_with_features`], but skips the per-match bounds check.
///
/// The inflate fast loop guarantees `INFLATE_FAST_MIN_LEFT` bytes of spare
/// capacity, which covers the longest match plus the chunked copy's overshoot,
/// so the check `copy_match_help` performs on every match is redundant there.
///
/// # Safety
///
/// `offset_from_end <= self.filled` and `self.filled + length + 32 <= self.capacity()`.
///
/// [`copy_match_with_features`]: Writer::copy_match_with_features
#[inline(always)]
pub unsafe fn copy_match_unchecked_with_features<const FEATURES: usize>(
&mut self,
offset_from_end: usize,
length: usize,
) {
match FEATURES {
#[cfg(target_arch = "x86_64")]
CpuFeatures::AVX2 => unsafe {
self.copy_match_unchecked::<32>(offset_from_end, length)
},
// Other targets keep the checked path; the margin only matters for the
// unchecked copy above.
_ => self.copy_match_runtime_dispatch(offset_from_end, length),
}
}

/// # Safety
///
/// `offset_from_end <= self.filled` and `self.filled + length + N <= self.capacity()`.
#[inline(always)]
unsafe fn copy_match_unchecked<const N: usize>(
&mut self,
offset_from_end: usize,
length: usize,
) {
let current = self.filled;
self.filled += length;
let ptr = self.buf.as_mut_ptr();

if length > offset_from_end {
match offset_from_end {
1 => {
// The referenced byte repeats; a slice `fill` lowers to `memset`.
// SAFETY: `current >= 1` and `current + length` is in bounds by the margin.
let element = unsafe { ptr.add(current - 1).read() };
let dst = unsafe { core::slice::from_raw_parts_mut(ptr.add(current), length) };
dst.fill(element);
}
_ => {
// Overlapping match with a small distance: byte-by-byte, no overshoot.
// SAFETY: reads trail writes by `offset_from_end`, all in bounds.
for i in 0..length {
let byte = unsafe { ptr.add(current - offset_from_end + i).read() };
unsafe { ptr.add(current + i).write(byte) };
}
}
}
} else {
// SAFETY: the caller guarantees `length + N` bytes of spare capacity, so the
// chunked copy's overshoot of at most `N` bytes stays in bounds.
unsafe {
Self::copy_chunk_unchecked::<N>(
ptr.add(current - offset_from_end),
ptr.add(current),
length,
)
}
}
}

#[inline(always)]
fn copy_match_help<const N: usize>(&mut self, offset_from_end: usize, length: usize) {
let capacity = self.buf.len();
Expand Down