* meaning.
*/
+/* Level 1 uses deflate_quick, which only reads the hash chain head, so it can
+ * skip prev maintenance. When quick is compiled out level 1 falls back to
+ * deflate_fast, which walks the chains like every other level. */
+#ifdef NO_QUICK_STRATEGY
+# define HAVE_QUICK_STRATEGY 0
+#else
+# define HAVE_QUICK_STRATEGY 1
+#endif
+
/* rank Z_BLOCK between Z_NO_FLUSH and Z_PARTIAL_FLUSH */
#define RANK(f) (((f) * 2) - ((f) > 4 ? 9 : 0))
int hashless = level == 0 || strategy == Z_HUFFMAN_ONLY || strategy == Z_RLE;
int was_hashless = s->level == 0 || s->strategy == Z_HUFFMAN_ONLY || s->strategy == Z_RLE;
- /* Stale if the hash usage flipped (to/from huffman/rle/stored) or the hash
- * function changed at level 9. */
- int stale_chain = (hashless != was_hashless) || (level >= 9) != (s->level >= 9);
+ /* Stale if the hash usage flipped (to/from huffman/rle/stored), the hash
+ * function changed at level 9, or quick at level 1 left prev unmaintained. */
+ int stale_chain = (hashless != was_hashless) || (level >= 9) != (s->level >= 9) ||
+ (HAVE_QUICK_STRATEGY && s->level == 1 && level != 1);
/* Rebuild the hash chains when fill_window is called. */
if (stale_chain && !hashless) {
if (level >= 9)
insert_batch = insert_roll_batch;
+ else if (HAVE_QUICK_STRATEGY && level == 1)
+ insert_batch = insert_knuth_batch_head;
else
insert_batch = insert_knuth_batch;
if (UNLIKELY(level >= 9)) {
s->ins_h = update_hash_roll(window[str], window[str+1]);
} else if (str >= 1) {
- insert_knuth(s, window, str + 2 - STD_MIN_MATCH);
+ if (HAVE_QUICK_STRATEGY && level == 1)
+ insert_knuth_head(s, window, str + 2 - STD_MIN_MATCH);
+ else
+ insert_knuth(s, window, str + 2 - STD_MIN_MATCH);
}
unsigned int count = s->insert;
if (UNLIKELY(s->lookahead == 1)) {
/* Type definitions for hash callbacks */
typedef struct internal_state deflate_state;
-typedef void (* insert_batch_func) (deflate_state *const s, unsigned char *window, uint32_t str, uint32_t count);
-void insert_knuth_batch (deflate_state *const s, unsigned char *window, uint32_t str, uint32_t count);
-void insert_roll_batch (deflate_state *const s, unsigned char *window, uint32_t str, uint32_t count);
+typedef void (* insert_batch_func) (deflate_state *const s, unsigned char *window, uint32_t str, uint32_t count);
+void insert_knuth_batch (deflate_state *const s, unsigned char *window, uint32_t str, uint32_t count);
+void insert_roll_batch (deflate_state *const s, unsigned char *window, uint32_t str, uint32_t count);
+void insert_knuth_batch_head (deflate_state *const s, unsigned char *window, uint32_t str, uint32_t count);
/* Struct for memory allocation handling */
typedef struct deflate_allocs_s {
uint32_t str_val = Z_U32_FROM_LE(zng_memread_4(window + strstart));
if (LIKELY(lookahead >= WANT_MIN_MATCH)) {
- uint32_t hash_head = insert_knuth_val(s, strstart, str_val);
+ uint32_t hash_head = insert_knuth_val_head(s, strstart, str_val);
int64_t dist = (int64_t)strstart - hash_head;
if (dist <= MAX_DIST(s) && dist > 0) {
Z_INTERNAL void insert_roll_batch(deflate_state *const s, unsigned char *window, uint32_t str, uint32_t count) {
insert_roll_batch_static(s, window, str, count);
}
+
+Z_INTERNAL void insert_knuth_batch_head(deflate_state *const s, unsigned char *window, uint32_t str, uint32_t count) {
+ insert_knuth_batch_head_static(s, window, str, count);
+}
return head;
}
+/* ===========================================================================
+ * Insert string str using a pre-read value, returning the previous head of the
+ * hash chain. The prev link is left untouched since deflate_quick only inspects
+ * the chain head and never walks the chain.
+ */
+Z_FORCEINLINE static uint32_t insert_knuth_val_head(deflate_state *const s, uint32_t str, uint32_t val) {
+ uint32_t h, head;
+
+ UPDATE_HASH_KNUTH(h, val);
+
+ head = s->head[h];
+ s->head[h] = (Pos)str;
+ return head;
+}
+
/* ===========================================================================
* Insert string str in the dictionary and set match_head to the previous head
* of the hash chain (the most recent string with same hash key). Return
return head;
}
+/* ===========================================================================
+ * Insert string str read from the window, returning the previous head of the
+ * hash chain. Like insert_knuth but leaves the prev link untouched for
+ * deflate_quick, which only inspects the chain head.
+ */
+Z_FORCEINLINE static uint32_t insert_knuth_head(deflate_state *const s, unsigned char *window, uint32_t str) {
+ uint8_t *strstart = window + str;
+ uint32_t val, h, head;
+
+ val = Z_U32_FROM_LE(zng_memread_4(strstart));
+ UPDATE_HASH_KNUTH(h, val);
+
+ head = s->head[h];
+ s->head[h] = (Pos)str;
+ return head;
+}
+
Z_FORCEINLINE static uint32_t insert_roll(deflate_state *const s, unsigned char *window, uint32_t str) {
uint8_t *strstart = window + str + (STD_MIN_MATCH-1);
uint32_t h, head;
}
}
+/* ===========================================================================
+ * Insert count strings read from the window, leaving the prev links untouched.
+ * Used by fill_window during deflate_quick, which only inspects the chain head.
+ */
+Z_FORCEINLINE static void insert_knuth_batch_head_static(deflate_state *const s, unsigned char *window, uint32_t str, uint32_t count) {
+ uint8_t *strstart = window + str;
+ uint8_t *strend = strstart + count;
+ Pos *headp = s->head;
+
+ for (uint32_t idx = str; strstart < strend; idx++, strstart++) {
+ uint32_t val, h;
+
+ val = Z_U32_FROM_LE(zng_memread_4(strstart));
+ UPDATE_HASH_KNUTH(h, val);
+
+ headp[h] = (Pos)idx;
+ }
+}
+
Z_FORCEINLINE static void insert_roll_batch_static(deflate_state *const s, unsigned char *window, uint32_t str, uint32_t count) {
uint8_t *strstart = window + str + (STD_MIN_MATCH-1);
uint8_t *strend = strstart + count;