You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
perf(encode): cap HC/BT history mirror near the live window (#421)
* perf(encode): cap HC/BT history mirror near the live window
Mirror the row (#418) and dfast (#420) history-buffer fix for the shared
HashChain / binary-tree MatchTable (btlazy2 / btopt / btultra, L13-L22).
On long streams the history mirror power-of-two doubled to ~2x the window
(an 8 MiB window peaked at 16 MiB), inflating compress peak memory. Reserve
window + window/4 + one block once eviction starts and drain the dead
prefix at a quarter window so the Vec grows linearly and never reallocates
again; reserve_history's ceiling is bumped to match. Small frames keep
their tight buffer. Byte-identical (buffer management only).
* refactor(encode): plain arithmetic for history cap, not saturating_add
The reserve_history ceiling used saturating_add, which would silently
clamp on overflow and mask an upstream window_log bound violation. The
sum is max_window_size + max_window_size/4 + MAX_BLOCK_SIZE with
max_window_size = 1 << window_log and window_log <= 31, so it is at most
~2^31 + 2^29 + 2^17 < usize::MAX even on 32-bit targets — overflow is
unreachable. Use plain arithmetic with a bound comment, matching the
add_data eviction reserve.
* refactor: drop defensive saturating arithmetic where overflow is unreachable
Codebase-wide audit of saturating_add/saturating_mul against the
safe-arithmetic policy (saturating only for a deliberate min/max clamp,
plain arithmetic when the bound provably holds, so a real overflow fails
loudly at its cause instead of being silently clamped).
Converted to plain arithmetic (with a bound comment each) where overflow
is unreachable: huffman tree node counts and literal/symbol frequency
counters (<= block size), pre-split fingerprint distance/threshold and
per-block event merge, FSE/HUF cost estimates, dict+window log helper
(matches upstream's plain u64 add), FASTCOVER epoch sizing, history
pre-size hint, fast-matcher length/2x-window checks, eviction byte
accounting.
Kept (documented as deliberate, not masking): compress_bound and the
decompressed-size upper bounds (saturate to the representable ceiling),
the optimal-parser price comparison (base_cost can be the u32::MAX
"unreachable" sentinel — saturating keeps the compare correct), the
skip-step loop-break idiom (saturate then `next <= pos` detects the end),
untrusted-frame size accumulation, and abs-position/index accumulators
that rely on the rebase machinery. Behavior-identical; 838 tests pass.
* test(encode): add regression for HC/BT reserve hint overflow
driver_huge_source_hint_with_dict_does_not_overflow_hc_reserve sets a
u64::MAX source-size hint (the unknown-size sentinel) plus a positive
dictionary hint and resets at Level 16 (BtOpt → HashChain/BT storage
arm). On current code `(src as usize) + dict_hint` overflows usize
before reserve_history can clamp: debug panic / release wrap on 64-bit,
src-truncation on 32-bit. Test fails (overflow panic) until the fix.
* fix(encode): saturate HC/BT reserve hint instead of overflowing
(src as usize) + dict_hint.unwrap_or(0) overflowed usize when src is
the u64::MAX unknown-size sentinel plus a positive dictionary hint, and
truncated src on 32-bit targets. Saturate src via usize::try_from and
saturate the dict-hint addition via checked_add; reserve_history applies
the tighter window ceiling. Fixes the panic in the regression test added
in the preceding commit.
* fix(encode): avoid eviction-check overflow on 32-bit; fix stale docs
FastKernelMatcher eviction computed real_len + space.len() before the
block-size assert; at window_log = 30 both terms approach cap = 2^31, so
the sum overflows usize on 32-bit targets. Reorder to compute cap, assert
the block bound, then compare via subtraction (real_len > cap -
space.len()) which the assert proves cannot underflow. Correct the stale
window_log <= 31 comment to the enforced <= 30, and refresh the
compact_history doc to describe the quarter-window / half-mirror drain.
* fix(encode): use saturating_add for HC/BT reserve hint (clippy)
* test(encode): assert clamped HC reserve ceiling
- Strengthen driver_huge_source_hint_with_dict_does_not_overflow_hc_reserve
to assert the post-reset history capacity reaches the clamped ceiling
(window + window/4 + block), not just no-panic; an under-reserved mirror
would otherwise pass.
- Correct the FastKernelMatcher eviction overflow-safety doc: the `* 2` is
safe because dictionary priming caps max_window_size at
MAX_PRIMED_WINDOW_SIZE = (u32::MAX - MAX_BLOCK_SIZE)/2 (so cap*2 < u32::MAX
by construction), not because window_log <= 30 (priming widens beyond that).
0 commit comments