1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
|
/* deflate_slow.c -- compress data using the slow strategy of deflation algorithm
*
* Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*/
#include "zbuild.h"
#include "deflate.h"
#include "deflate_p.h"
#include "functable.h"
/* ===========================================================================
* Same as deflate_medium, but achieves better compression. We use a lazy
* evaluation for matches: a match is finally adopted only if there is
* no better match at the next window position.
*/
Z_INTERNAL block_state deflate_slow(deflate_state *s, int flush) {
Pos hash_head; /* head of hash chain */
int bflush; /* set if current block must be flushed */
int64_t dist;
uint32_t match_len;
match_func longest_match;
if (s->max_chain_length <= 1024)
longest_match = FUNCTABLE_FPTR(longest_match);
else
longest_match = FUNCTABLE_FPTR(longest_match_slow);
/* Process the input block. */
for (;;) {
/* Make sure that we always have enough lookahead, except
* at the end of the input file. We need STD_MAX_MATCH bytes
* for the next match, plus WANT_MIN_MATCH bytes to insert the
* string following the next match.
*/
if (s->lookahead < MIN_LOOKAHEAD) {
PREFIX(fill_window)(s);
if (UNLIKELY(s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH)) {
return need_more;
}
if (UNLIKELY(s->lookahead == 0))
break; /* flush the current block */
}
/* Insert the string window[strstart .. strstart+2] in the
* dictionary, and set hash_head to the head of the hash chain:
*/
hash_head = 0;
if (LIKELY(s->lookahead >= WANT_MIN_MATCH)) {
hash_head = s->quick_insert_string(s, s->strstart);
}
/* Find the longest match, discarding those <= prev_length.
*/
s->prev_match = (Pos)s->match_start;
match_len = STD_MIN_MATCH - 1;
dist = (int64_t)s->strstart - hash_head;
if (dist <= MAX_DIST(s) && dist > 0 && s->prev_length < s->max_lazy_match && hash_head != 0) {
/* To simplify the code, we prevent matches with the string
* of window index 0 (in particular we have to avoid a match
* of the string with itself at the start of the input file).
*/
match_len = longest_match(s, hash_head);
/* longest_match() sets match_start */
if (match_len <= 5 && (s->strategy == Z_FILTERED)) {
/* If prev_match is also WANT_MIN_MATCH, match_start is garbage
* but we will ignore the current match anyway.
*/
match_len = STD_MIN_MATCH - 1;
}
}
/* If there was a match at the previous step and the current
* match is not better, output the previous match:
*/
if (s->prev_length >= STD_MIN_MATCH && match_len <= s->prev_length) {
unsigned int max_insert = s->strstart + s->lookahead - STD_MIN_MATCH;
/* Do not insert strings in hash table beyond this. */
Assert((s->strstart-1) <= UINT16_MAX, "strstart-1 should fit in uint16_t");
check_match(s, (Pos)(s->strstart - 1), s->prev_match, s->prev_length);
bflush = zng_tr_tally_dist(s, s->strstart -1 - s->prev_match, s->prev_length - STD_MIN_MATCH);
/* Insert in hash table all strings up to the end of the match.
* strstart-1 and strstart are already inserted. If there is not
* enough lookahead, the last two strings are not inserted in
* the hash table.
*/
s->prev_length -= 1;
s->lookahead -= s->prev_length;
unsigned int mov_fwd = s->prev_length - 1;
if (max_insert > s->strstart) {
unsigned int insert_cnt = mov_fwd;
if (UNLIKELY(insert_cnt > max_insert - s->strstart))
insert_cnt = max_insert - s->strstart;
s->insert_string(s, s->strstart + 1, insert_cnt);
}
s->prev_length = 0;
s->match_available = 0;
s->strstart += mov_fwd + 1;
if (UNLIKELY(bflush))
FLUSH_BLOCK(s, 0);
} else if (s->match_available) {
/* If there was no match at the previous position, output a
* single literal. If there was a match but the current match
* is longer, truncate the previous match to a single literal.
*/
bflush = zng_tr_tally_lit(s, s->window[s->strstart-1]);
if (UNLIKELY(bflush))
FLUSH_BLOCK_ONLY(s, 0);
s->prev_length = match_len;
s->strstart++;
s->lookahead--;
if (UNLIKELY(s->strm->avail_out == 0))
return need_more;
} else {
/* There is no previous match to compare with, wait for
* the next step to decide.
*/
s->prev_length = match_len;
s->match_available = 1;
s->strstart++;
s->lookahead--;
}
}
Assert(flush != Z_NO_FLUSH, "no flush?");
if (UNLIKELY(s->match_available)) {
Z_UNUSED(zng_tr_tally_lit(s, s->window[s->strstart-1]));
s->match_available = 0;
}
s->insert = s->strstart < (STD_MIN_MATCH - 1) ? s->strstart : (STD_MIN_MATCH - 1);
if (UNLIKELY(flush == Z_FINISH)) {
FLUSH_BLOCK(s, 1);
return finish_done;
}
if (UNLIKELY(s->sym_next))
FLUSH_BLOCK(s, 0);
return block_done;
}
|