]> git.ipfire.org Git - thirdparty/zlib-ng.git/commitdiff
deflate: add new deflate_medium strategy
authorJim Kukunas <james.t.kukunas@linux.intel.com>
Thu, 18 Jul 2013 22:45:18 +0000 (15:45 -0700)
committerJim Kukunas <james.t.kukunas@linux.intel.com>
Sat, 26 Jul 2014 22:53:17 +0000 (15:53 -0700)
From: Arjan van de Ven <arjan@linux.intel.com>

As the name suggests, the deflate_medium deflate strategy is designed
to provide an intermediate strategy between deflate_fast and deflate_slow.
After finding two adjacent matches, deflate_medium scans left from
the second match in order to determine whether a better match can be
formed.

Fixes #2

configure
deflate.c
deflate_medium.c [new file with mode: 0644]

index a42da63d59b9a9f28a5a604b82223a342de052c3..b68e57ba2071f6560021450fa45538babc0d8e1f 100755 (executable)
--- a/configure
+++ b/configure
@@ -841,8 +841,8 @@ case "${ARCH}" in
             CRC_FOLDING_lo=""
         fi
 
-        CFLAGS="${CFLAGS} -DUSE_QUICK"
-        SFLAGS="${SFLAGS} -DUSE_QUICK"
+        CFLAGS="${CFLAGS} -DUSE_QUICK -DUSE_MEDIUM"
+        SFLAGS="${SFLAGS} -DUSE_QUICK -DUSE_MEDIUM"
 
         DEFLATE_QUICK_o="deflate_quick.o"
         DEFLATE_QUICK_lo="deflate_quick.lo"
@@ -887,8 +887,8 @@ case "${ARCH}" in
             CRC_FOLDING_lo=""
         fi
 
-        CFLAGS="${CFLAGS} -DUSE_QUICK"
-        SFLAGS="${SFLAGS} -DUSE_QUICK"
+        CFLAGS="${CFLAGS} -DUSE_QUICK -DUSE_MEDIUM"
+        SFLAGS="${SFLAGS} -DUSE_QUICK -DUSE_MEDIUM"
 
         DEFLATE_QUICK_o="deflate_quick.o"
         DEFLATE_QUICK_lo="deflate_quick.lo"
index fb66878760bcbc95b33f05b1280ab6a17ddcd113..c1147aaab5d9856cb1a0663f480489579638be32 100644 (file)
--- a/deflate.c
+++ b/deflate.c
@@ -75,6 +75,7 @@ local void fill_window    OF((deflate_state *s));
 local block_state deflate_stored OF((deflate_state *s, int flush));
 local block_state deflate_fast   OF((deflate_state *s, int flush));
 block_state deflate_quick  OF((deflate_state *s, int flush));
+local block_state deflate_medium OF((deflate_state *s, int flush));
 #ifndef FASTEST
 local block_state deflate_slow   OF((deflate_state *s, int flush));
 #endif
@@ -140,9 +141,16 @@ local const config configuration_table[10] = {
 /* 3 */ {4,    6, 32,   32, deflate_fast},
 #endif
 
+#ifdef USE_MEDIUM
+/* 4 */ {4,    4, 16,   16, deflate_medium},  /* lazy matches */
+/* 5 */ {8,   16, 32,   32, deflate_medium},
+/* 6 */ {8,   16, 128, 128, deflate_medium},
+#else
 /* 4 */ {4,    4, 16,   16, deflate_slow},  /* lazy matches */
 /* 5 */ {8,   16, 32,   32, deflate_slow},
 /* 6 */ {8,   16, 128, 128, deflate_slow},
+#endif
+
 /* 7 */ {8,   32, 128, 256, deflate_slow},
 /* 8 */ {32, 128, 258, 1024, deflate_slow},
 /* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */
@@ -1595,6 +1603,10 @@ local block_state deflate_fast(s, flush)
 }
 
 
+#ifdef USE_MEDIUM
+#include "deflate_medium.c"
+#endif
+
 #ifndef FASTEST
 /* ===========================================================================
  * Same as above, but achieves better compression. We use a lazy
diff --git a/deflate_medium.c b/deflate_medium.c
new file mode 100644 (file)
index 0000000..ce6bd97
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * The deflate_medium deflate strategy
+ *
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ * Authors:
+ *  Arjan van de Ven    <arjan@linux.intel.com>
+ *
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+#include "deflate.h"
+
+struct match {
+    uInt    match_start;
+    uInt    match_length;    
+    uInt    strstart;
+    uInt    orgstart;
+};
+
+#define MAX_DIST2  ((1 << MAX_WBITS) - MIN_LOOKAHEAD)
+
+static int tr_tally_dist(deflate_state *s, int distance, int length)
+{
+    return _tr_tally(s, distance, length);
+}
+
+static int tr_tally_lit(deflate_state *s, int c)
+{
+    return  _tr_tally(s, 0, c);
+}
+
+static int emit_match(deflate_state *s, struct match match, IPos hash_head)
+{
+        int flush = 0;
+        
+        /* matches that are not long enough we need to emit as litterals */
+        if (match.match_length < MIN_MATCH) {
+            while (match.match_length) {
+                    flush += tr_tally_lit (s, s->window[match.strstart]);
+                    s->lookahead--;
+                    match.strstart++;
+                    match.match_length--;
+            }
+            return flush;
+        }
+
+        check_match(s, match.strstart, match.match_start, match.match_length);
+
+        flush += tr_tally_dist(s, match.strstart - match.match_start,
+                           match.match_length - MIN_MATCH);
+
+        s->lookahead -= match.match_length;
+        return flush;
+}
+
+static void insert_match(deflate_state *s, struct match match)
+{
+    if (zunlikely(s->lookahead <= match.match_length + MIN_MATCH))
+        return;
+
+        /* matches that are not long enough we need to emit as litterals */
+        if (match.match_length < MIN_MATCH) {
+            while (match.match_length) {
+                    match.strstart++;
+                    match.match_length--;
+
+                    if (match.match_length) {
+                        if (match.strstart >= match.orgstart) {
+                            insert_string(s, match.strstart);
+                        }
+                    }
+            }
+            return;
+        }
+
+        /* Insert new strings in the hash table only if the match length
+         * is not too large. This saves time but degrades compression.
+         */
+        if (match.match_length <= 16* s->max_insert_length &&
+                    s->lookahead >= MIN_MATCH) {
+                match.match_length--; /* string at strstart already in table */
+                do {
+                        match.strstart++;
+                        if (zlikely(match.strstart >= match.orgstart)) {
+                            insert_string(s, match.strstart);
+                        }
+                    /* strstart never exceeds WSIZE-MAX_MATCH, so there are
+                     * always MIN_MATCH bytes ahead.
+                     */
+                } while (--match.match_length != 0);
+                match.strstart++;
+        } else {
+                match.strstart += match.match_length;
+                match.match_length = 0;
+                s->ins_h = s->window[match.strstart];
+                if (match.strstart >= 1)
+                    UPDATE_HASH(s, s->ins_h, match.strstart+2-MIN_MATCH);
+#if MIN_MATCH != 3
+#warning Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+        /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
+         * matter since it will be recomputed at next deflate call.
+         */
+        }
+}
+
+static void fizzle_matches(deflate_state *s, struct match *current, struct match *next)
+{
+    IPos limit;
+    unsigned char *match, *orig;
+    int changed = 0;
+    struct match c,n;
+    /* step zero: sanity checks */
+
+    if (current->match_length <= 1)
+            return;
+
+    match = s->window - current->match_length + 1 + next->match_start ;
+    orig  = s->window - current->match_length + 1 + next->strstart ;
+    
+    /* quick exit check.. if this fails then don't bother with anything else */
+    if (zlikely(*match != *orig))
+        return;
+
+    /*
+     * check the overlap case and just give up. We can do better in theory,
+     * but unlikely to be worth it 
+     */
+    if (next->match_start + next->match_length >= current->strstart)
+            return;
+        
+    c = *current;
+    n = *next;
+
+    /* step one: try to move the "next" match to the left as much as possible */
+    limit = next->strstart > MAX_DIST2 ? next->strstart - MAX_DIST2 : 0;
+     
+    match = s->window + n.match_start - 1;
+    orig = s->window + n.strstart - 1;
+
+    while (*match == *orig) {
+        if (c.match_length < 1)
+            break;
+        if (n.strstart <= limit)
+            break;
+        if (n.match_length >= 256)
+            break;
+        if (n.match_start <= 0)
+            break;
+
+        n.strstart--;
+        n.match_start--;
+        n.match_length++;
+        c.match_length--;
+        match--;
+        orig--;
+        changed ++;
+    }
+
+    if (!changed)
+        return;
+
+    if ( (c.match_length <= 1) && n.match_length != 2) {
+        n.orgstart++;
+        *current = c;
+        *next = n;
+    } else return;
+}
+
+block_state deflate_medium(deflate_state *s, int flush)
+{
+    struct match current_match, next_match;
+    
+    memset(&current_match, 0, sizeof(struct match));
+    memset(&next_match, 0, sizeof(struct match));
+
+    for (;;) {
+        IPos hash_head;       /* head of the hash chain */
+        int bflush;           /* set if current block must be flushed */
+        
+        /* Make sure that we always have enough lookahead, except
+         * at the end of the input file. We need MAX_MATCH bytes
+         * for the next match, plus MIN_MATCH bytes to insert the
+         * string following the next current_match.
+         */
+        if (s->lookahead < MIN_LOOKAHEAD) {
+            fill_window(s);
+            if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+                return need_more;
+            }
+            if (s->lookahead == 0) break; /* flush the current block */
+            next_match.match_length = 0;
+        }
+        s->prev_length = 2;
+
+        /* Insert the string window[strstart .. strstart+2] in the
+         * dictionary, and set hash_head to the head of the hash chain:
+         */
+        
+        /* If we already have a future match from a previous round, just use that */
+        if (next_match.match_length > 0) {
+            current_match = next_match;
+            next_match.match_length = 0;
+
+        } else {
+            hash_head = 0;
+            if (s->lookahead >= MIN_MATCH) {
+                hash_head = insert_string(s, s->strstart);
+            }
+        
+            /* set up the initial match to be a 1 byte literal */
+            current_match.match_start = 0;
+            current_match.match_length = 1;
+            current_match.strstart = s->strstart;
+            current_match.orgstart = current_match.strstart;
+        
+            /* Find the longest match, discarding those <= prev_length.
+             * At this point we have always match_length < MIN_MATCH
+             */
+             
+            if (hash_head != 0 && s->strstart - hash_head <= MAX_DIST2) {
+                /* 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).
+                 */
+                current_match.match_length = longest_match (s, hash_head);
+                current_match.match_start = s->match_start;
+                if (current_match.match_length < MIN_MATCH)
+                    current_match.match_length = 1;
+                if (current_match.match_start >= current_match.strstart) { 
+                    /* this can happen due to some restarts */
+                    current_match.match_length = 1;
+                }
+            }
+        }
+        
+        insert_match(s, current_match);
+
+        /* now, look ahead one */
+        if (s->lookahead > MIN_LOOKAHEAD) {
+            s->strstart = current_match.strstart + current_match.match_length;
+            hash_head = insert_string(s, s->strstart);
+        
+            /* set up the initial match to be a 1 byte literal */
+            next_match.match_start = 0;
+            next_match.match_length = 1;
+            next_match.strstart = s->strstart;
+            next_match.orgstart = next_match.strstart;
+
+            /* Find the longest match, discarding those <= prev_length.
+             * At this point we have always match_length < MIN_MATCH
+             */
+            if (hash_head != 0 && s->strstart - hash_head <= MAX_DIST2) {
+                /* 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).
+                 */
+                next_match.match_length = longest_match (s, hash_head);
+                next_match.match_start = s->match_start;
+                if (next_match.match_start >= next_match.strstart)
+                    /* this can happen due to some restarts */
+                    next_match.match_length = 1;
+                if (next_match.match_length < MIN_MATCH)
+                    next_match.match_length = 1;
+                else
+                    fizzle_matches(s, &current_match, &next_match);
+            }
+            
+            /* short matches with a very long distance are rarely a good idea encoding wise */
+            if (next_match.match_length == 3 &&
+            (next_match.strstart - next_match.match_start) > 12000)
+                    next_match.match_length = 1;
+            s->strstart = current_match.strstart;
+        
+        } else {
+            next_match.match_length = 0;
+        }
+        
+        /* now emit the current match */
+        bflush = emit_match(s, current_match, hash_head);
+        
+        /* move the "cursor" forward */
+        s->strstart += current_match.match_length;        
+        
+        if (bflush)
+            FLUSH_BLOCK(s, 0);
+    }
+    s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1;
+    if (flush == Z_FINISH) {
+        FLUSH_BLOCK(s, 1);
+        return finish_done;
+    }
+    if (s->last_lit)
+        FLUSH_BLOCK(s, 0);
+    return block_done;
+}