From 7b44ddac58dd28ed6d044b4b69f8c6a95b66f15d Mon Sep 17 00:00:00 2001 From: LibretroAdmin Date: Sat, 7 Nov 2026 19:27:01 +0100 Subject: [PATCH] rdeflate: the lazy parser skips the match finder on an empty chain rd_insert() hands back the chain head it just linked for a position, and the greedy parser (levels 1-3) already used it to skip the match search when there is no earlier occurrence. The lazy parser - levels 4-9, which includes the level-6 screenshot encoder - threw it away and called rd_longest_match() at every position, which reloaded the head from prev[] and returned 0 when it was empty. On noisy input that is about half of all positions: a full call, register saves and the struct loads of the prologue, to learn there was nothing to do. Both parsers now pass the head in and only call when a chain exists; rd_longest_match() starts its walk from it instead of re-reading prev[]. An empty chain returned 0 with no side effects before, so the parse - and the stream - are unchanged. Compressed output is byte-identical to before over the level 0-9 x input chunking x six-input matrix, zlib-wrapped and raw, also under ASan/UBSan; rzip, rpng, archive and encodings sample tests pass; builds C89 without new warnings and for aarch64. screenshot_io_bench, 3840x2160 BGR24 PNG: noise instructions -10.3% (10.37 G -> 9.30 G), best 393 -> 380 ms, median about -4%; gradient unchanged. --- libretro-common/encodings/encoding_deflate.c | 34 ++++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/libretro-common/encodings/encoding_deflate.c b/libretro-common/encodings/encoding_deflate.c index f60e7bc931..94ef10be64 100644 --- a/libretro-common/encodings/encoding_deflate.c +++ b/libretro-common/encodings/encoding_deflate.c @@ -2587,8 +2587,12 @@ static INLINE uint32_t rd_insert_end(uint32_t stop, uint32_t end) return stop < lim ? stop : lim; } +/* `first` is the chain head rd_insert() returned for `pos` - the + * previous occurrence, zero-based - which the caller has already found + * to exist; an empty chain never gets here. */ static INLINE uint32_t rd_longest_match(struct rdeflate *s, uint32_t pos, - uint32_t max_len, uint32_t best_start, uint32_t *dist_out) + int32_t first, uint32_t max_len, uint32_t best_start, + uint32_t *dist_out) { int chain; uint32_t limit; @@ -2597,13 +2601,10 @@ static INLINE uint32_t rd_longest_match(struct rdeflate *s, uint32_t pos, const uint8_t *scan = win + pos; uint32_t best_len = best_start; /* only a longer match is interesting */ uint32_t best_dist = 0; - /* The caller has already inserted `pos`; begin at the previous - * occurrence recorded in prev[]. Chain links are stored as - * position+1 with 0 for "empty" (see rd_insert), so `cur` is - * one-based throughout this walk and `cpos` is the position. */ - int32_t cur = s->prev[pos & RD_WMASK]; - if (cur == 0) - return 0; + /* Chain links are stored as position+1 with 0 for "empty" (see + * rd_insert), so `cur` is one-based throughout this walk and `cpos` + * is the position. */ + int32_t cur = first + 1; { const int32_t *prev = s->prev; /* hoisted: avoids repeated struct load */ @@ -2892,8 +2893,9 @@ static void rd_parse(struct rdeflate *s) { /* insert returns the previous head of this hash chain; only * bother searching when the chain is non-empty. */ - if (rd_insert(s, s->pos) >= 0) - mlen = rd_longest_match(s, s->pos, max_len, + int32_t first = rd_insert(s, s->pos); + if (first >= 0) + mlen = rd_longest_match(s, s->pos, first, max_len, (uint32_t)(RD_MIN_MATCH - 1), &mdist); } if (mlen >= RD_MIN_MATCH) @@ -2939,12 +2941,16 @@ static void rd_parse(struct rdeflate *s) /* insert the current position into the hash chain and find a match */ if (max_len >= RD_MIN_MATCH) { - rd_insert(s, s->pos); + /* An empty chain - about half of all positions on noisy + * input - has nothing to search, so the match finder is not + * entered for it; it would only have returned 0. */ + int32_t first = rd_insert(s, s->pos); /* When a match is pending from the previous byte, only a strictly * longer match here matters -- start the search bar at prev_len. */ - mlen = rd_longest_match(s, s->pos, max_len, - s->have_prev ? s->prev_len : (uint32_t)(RD_MIN_MATCH - 1), - &mdist); + if (first >= 0) + mlen = rd_longest_match(s, s->pos, first, max_len, + s->have_prev ? s->prev_len : (uint32_t)(RD_MIN_MATCH - 1), + &mdist); } if (s->have_prev)