]> pd.if.org Git - zpackage/blobdiff - lzma/lz/lz_encoder_mf.c
integrate lzma
[zpackage] / lzma / lz / lz_encoder_mf.c
diff --git a/lzma/lz/lz_encoder_mf.c b/lzma/lz/lz_encoder_mf.c
new file mode 100644 (file)
index 0000000..7852077
--- /dev/null
@@ -0,0 +1,744 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+/// \file       lz_encoder_mf.c
+/// \brief      Match finders
+///
+//  Authors:    Igor Pavlov
+//              Lasse Collin
+//
+//  This file has been put into the public domain.
+//  You can do whatever you want with this file.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "lz_encoder.h"
+#include "lz_encoder_hash.h"
+#include "memcmplen.h"
+
+
+/// \brief      Find matches starting from the current byte
+///
+/// \return     The length of the longest match found
+extern uint32_t
+lzma_mf_find(lzma_mf *mf, uint32_t *count_ptr, lzma_match *matches)
+{
+       // Call the match finder. It returns the number of length-distance
+       // pairs found.
+       // FIXME: Minimum count is zero, what _exactly_ is the maximum?
+       const uint32_t count = mf->find(mf, matches);
+
+       // Length of the longest match; assume that no matches were found
+       // and thus the maximum length is zero.
+       uint32_t len_best = 0;
+
+       if (count > 0) {
+#ifndef NDEBUG
+               // Validate the matches.
+               for (uint32_t i = 0; i < count; ++i) {
+                       assert(matches[i].len <= mf->nice_len);
+                       assert(matches[i].dist < mf->read_pos);
+                       assert(memcmp(mf_ptr(mf) - 1,
+                               mf_ptr(mf) - matches[i].dist - 2,
+                               matches[i].len) == 0);
+               }
+#endif
+
+               // The last used element in the array contains
+               // the longest match.
+               len_best = matches[count - 1].len;
+
+               // If a match of maximum search length was found, try to
+               // extend the match to maximum possible length.
+               if (len_best == mf->nice_len) {
+                       // The limit for the match length is either the
+                       // maximum match length supported by the LZ-based
+                       // encoder or the number of bytes left in the
+                       // dictionary, whichever is smaller.
+                       uint32_t limit = mf_avail(mf) + 1;
+                       if (limit > mf->match_len_max)
+                               limit = mf->match_len_max;
+
+                       // Pointer to the byte we just ran through
+                       // the match finder.
+                       const uint8_t *p1 = mf_ptr(mf) - 1;
+
+                       // Pointer to the beginning of the match. We need -1
+                       // here because the match distances are zero based.
+                       const uint8_t *p2 = p1 - matches[count - 1].dist - 1;
+
+                       len_best = lzma_memcmplen(p1, p2, len_best, limit);
+               }
+       }
+
+       *count_ptr = count;
+
+       // Finally update the read position to indicate that match finder was
+       // run for this dictionary offset.
+       ++mf->read_ahead;
+
+       return len_best;
+}
+
+
+/// Hash value to indicate unused element in the hash. Since we start the
+/// positions from dict_size + 1, zero is always too far to qualify
+/// as usable match position.
+#define EMPTY_HASH_VALUE 0
+
+
+/// Normalization must be done when lzma_mf.offset + lzma_mf.read_pos
+/// reaches MUST_NORMALIZE_POS.
+#define MUST_NORMALIZE_POS UINT32_MAX
+
+
+/// \brief      Normalizes hash values
+///
+/// The hash arrays store positions of match candidates. The positions are
+/// relative to an arbitrary offset that is not the same as the absolute
+/// offset in the input stream. The relative position of the current byte
+/// is lzma_mf.offset + lzma_mf.read_pos. The distances of the matches are
+/// the differences of the current read position and the position found from
+/// the hash.
+///
+/// To prevent integer overflows of the offsets stored in the hash arrays,
+/// we need to "normalize" the stored values now and then. During the
+/// normalization, we drop values that indicate distance greater than the
+/// dictionary size, thus making space for new values.
+static void
+normalize(lzma_mf *mf)
+{
+       assert(mf->read_pos + mf->offset == MUST_NORMALIZE_POS);
+
+       // In future we may not want to touch the lowest bits, because there
+       // may be match finders that use larger resolution than one byte.
+       const uint32_t subvalue
+                       = (MUST_NORMALIZE_POS - mf->cyclic_size);
+                               // & (~(UINT32_C(1) << 10) - 1);
+
+       for (uint32_t i = 0; i < mf->hash_count; ++i) {
+               // If the distance is greater than the dictionary size,
+               // we can simply mark the hash element as empty.
+               if (mf->hash[i] <= subvalue)
+                       mf->hash[i] = EMPTY_HASH_VALUE;
+               else
+                       mf->hash[i] -= subvalue;
+       }
+
+       for (uint32_t i = 0; i < mf->sons_count; ++i) {
+               // Do the same for mf->son.
+               //
+               // NOTE: There may be uninitialized elements in mf->son.
+               // Valgrind may complain that the "if" below depends on
+               // an uninitialized value. In this case it is safe to ignore
+               // the warning. See also the comments in lz_encoder_init()
+               // in lz_encoder.c.
+               if (mf->son[i] <= subvalue)
+                       mf->son[i] = EMPTY_HASH_VALUE;
+               else
+                       mf->son[i] -= subvalue;
+       }
+
+       // Update offset to match the new locations.
+       mf->offset -= subvalue;
+
+       return;
+}
+
+
+/// Mark the current byte as processed from point of view of the match finder.
+static void
+move_pos(lzma_mf *mf)
+{
+       if (++mf->cyclic_pos == mf->cyclic_size)
+               mf->cyclic_pos = 0;
+
+       ++mf->read_pos;
+       assert(mf->read_pos <= mf->write_pos);
+
+       if (unlikely(mf->read_pos + mf->offset == UINT32_MAX))
+               normalize(mf);
+}
+
+
+/// When flushing, we cannot run the match finder unless there is nice_len
+/// bytes available in the dictionary. Instead, we skip running the match
+/// finder (indicating that no match was found), and count how many bytes we
+/// have ignored this way.
+///
+/// When new data is given after the flushing was completed, the match finder
+/// is restarted by rewinding mf->read_pos backwards by mf->pending. Then
+/// the missed bytes are added to the hash using the match finder's skip
+/// function (with small amount of input, it may start using mf->pending
+/// again if flushing).
+///
+/// Due to this rewinding, we don't touch cyclic_pos or test for
+/// normalization. It will be done when the match finder's skip function
+/// catches up after a flush.
+static void
+move_pending(lzma_mf *mf)
+{
+       ++mf->read_pos;
+       assert(mf->read_pos <= mf->write_pos);
+       ++mf->pending;
+}
+
+
+/// Calculate len_limit and determine if there is enough input to run
+/// the actual match finder code. Sets up "cur" and "pos". This macro
+/// is used by all find functions and binary tree skip functions. Hash
+/// chain skip function doesn't need len_limit so a simpler code is used
+/// in them.
+#define header(is_bt, len_min, ret_op) \
+       uint32_t len_limit = mf_avail(mf); \
+       if (mf->nice_len <= len_limit) { \
+               len_limit = mf->nice_len; \
+       } else if (len_limit < (len_min) \
+                       || (is_bt && mf->action == LZMA_SYNC_FLUSH)) { \
+               assert(mf->action != LZMA_RUN); \
+               move_pending(mf); \
+               ret_op; \
+       } \
+       const uint8_t *cur = mf_ptr(mf); \
+       const uint32_t pos = mf->read_pos + mf->offset
+
+
+/// Header for find functions. "return 0" indicates that zero matches
+/// were found.
+#define header_find(is_bt, len_min) \
+       header(is_bt, len_min, return 0); \
+       uint32_t matches_count = 0
+
+
+/// Header for a loop in a skip function. "continue" tells to skip the rest
+/// of the code in the loop.
+#define header_skip(is_bt, len_min) \
+       header(is_bt, len_min, continue)
+
+
+/// Calls hc_find_func() or bt_find_func() and calculates the total number
+/// of matches found. Updates the dictionary position and returns the number
+/// of matches found.
+#define call_find(func, len_best) \
+do { \
+       matches_count = func(len_limit, pos, cur, cur_match, mf->depth, \
+                               mf->son, mf->cyclic_pos, mf->cyclic_size, \
+                               matches + matches_count, len_best) \
+                       - matches; \
+       move_pos(mf); \
+       return matches_count; \
+} while (0)
+
+
+////////////////
+// Hash Chain //
+////////////////
+
+#if defined(HAVE_MF_HC3) || defined(HAVE_MF_HC4)
+///
+///
+/// \param      len_limit       Don't look for matches longer than len_limit.
+/// \param      pos             lzma_mf.read_pos + lzma_mf.offset
+/// \param      cur             Pointer to current byte (mf_ptr(mf))
+/// \param      cur_match       Start position of the current match candidate
+/// \param      depth           Maximum length of the hash chain
+/// \param      son             lzma_mf.son (contains the hash chain)
+/// \param      cyclic_pos
+/// \param      cyclic_size
+/// \param      matches         Array to hold the matches.
+/// \param      len_best        The length of the longest match found so far.
+static lzma_match *
+hc_find_func(
+               const uint32_t len_limit,
+               const uint32_t pos,
+               const uint8_t *const cur,
+               uint32_t cur_match,
+               uint32_t depth,
+               uint32_t *const son,
+               const uint32_t cyclic_pos,
+               const uint32_t cyclic_size,
+               lzma_match *matches,
+               uint32_t len_best)
+{
+       son[cyclic_pos] = cur_match;
+
+       while (true) {
+               const uint32_t delta = pos - cur_match;
+               if (depth-- == 0 || delta >= cyclic_size)
+                       return matches;
+
+               const uint8_t *const pb = cur - delta;
+               cur_match = son[cyclic_pos - delta
+                               + (delta > cyclic_pos ? cyclic_size : 0)];
+
+               if (pb[len_best] == cur[len_best] && pb[0] == cur[0]) {
+                       uint32_t len = lzma_memcmplen(pb, cur, 1, len_limit);
+
+                       if (len_best < len) {
+                               len_best = len;
+                               matches->len = len;
+                               matches->dist = delta - 1;
+                               ++matches;
+
+                               if (len == len_limit)
+                                       return matches;
+                       }
+               }
+       }
+}
+
+
+#define hc_find(len_best) \
+       call_find(hc_find_func, len_best)
+
+
+#define hc_skip() \
+do { \
+       mf->son[mf->cyclic_pos] = cur_match; \
+       move_pos(mf); \
+} while (0)
+
+#endif
+
+
+#ifdef HAVE_MF_HC3
+extern uint32_t
+lzma_mf_hc3_find(lzma_mf *mf, lzma_match *matches)
+{
+       header_find(false, 3);
+
+       hash_3_calc();
+
+       const uint32_t delta2 = pos - mf->hash[hash_2_value];
+       const uint32_t cur_match = mf->hash[FIX_3_HASH_SIZE + hash_value];
+
+       mf->hash[hash_2_value] = pos;
+       mf->hash[FIX_3_HASH_SIZE + hash_value] = pos;
+
+       uint32_t len_best = 2;
+
+       if (delta2 < mf->cyclic_size && *(cur - delta2) == *cur) {
+               len_best = lzma_memcmplen(cur - delta2, cur,
+                               len_best, len_limit);
+
+               matches[0].len = len_best;
+               matches[0].dist = delta2 - 1;
+               matches_count = 1;
+
+               if (len_best == len_limit) {
+                       hc_skip();
+                       return 1; // matches_count
+               }
+       }
+
+       hc_find(len_best);
+}
+
+
+extern void
+lzma_mf_hc3_skip(lzma_mf *mf, uint32_t amount)
+{
+       do {
+               if (mf_avail(mf) < 3) {
+                       move_pending(mf);
+                       continue;
+               }
+
+               const uint8_t *cur = mf_ptr(mf);
+               const uint32_t pos = mf->read_pos + mf->offset;
+
+               hash_3_calc();
+
+               const uint32_t cur_match
+                               = mf->hash[FIX_3_HASH_SIZE + hash_value];
+
+               mf->hash[hash_2_value] = pos;
+               mf->hash[FIX_3_HASH_SIZE + hash_value] = pos;
+
+               hc_skip();
+
+       } while (--amount != 0);
+}
+#endif
+
+
+#ifdef HAVE_MF_HC4
+extern uint32_t
+lzma_mf_hc4_find(lzma_mf *mf, lzma_match *matches)
+{
+       header_find(false, 4);
+
+       hash_4_calc();
+
+       uint32_t delta2 = pos - mf->hash[hash_2_value];
+       const uint32_t delta3
+                       = pos - mf->hash[FIX_3_HASH_SIZE + hash_3_value];
+       const uint32_t cur_match = mf->hash[FIX_4_HASH_SIZE + hash_value];
+
+       mf->hash[hash_2_value ] = pos;
+       mf->hash[FIX_3_HASH_SIZE + hash_3_value] = pos;
+       mf->hash[FIX_4_HASH_SIZE + hash_value] = pos;
+
+       uint32_t len_best = 1;
+
+       if (delta2 < mf->cyclic_size && *(cur - delta2) == *cur) {
+               len_best = 2;
+               matches[0].len = 2;
+               matches[0].dist = delta2 - 1;
+               matches_count = 1;
+       }
+
+       if (delta2 != delta3 && delta3 < mf->cyclic_size
+                       && *(cur - delta3) == *cur) {
+               len_best = 3;
+               matches[matches_count++].dist = delta3 - 1;
+               delta2 = delta3;
+       }
+
+       if (matches_count != 0) {
+               len_best = lzma_memcmplen(cur - delta2, cur,
+                               len_best, len_limit);
+
+               matches[matches_count - 1].len = len_best;
+
+               if (len_best == len_limit) {
+                       hc_skip();
+                       return matches_count;
+               }
+       }
+
+       if (len_best < 3)
+               len_best = 3;
+
+       hc_find(len_best);
+}
+
+
+extern void
+lzma_mf_hc4_skip(lzma_mf *mf, uint32_t amount)
+{
+       do {
+               if (mf_avail(mf) < 4) {
+                       move_pending(mf);
+                       continue;
+               }
+
+               const uint8_t *cur = mf_ptr(mf);
+               const uint32_t pos = mf->read_pos + mf->offset;
+
+               hash_4_calc();
+
+               const uint32_t cur_match
+                               = mf->hash[FIX_4_HASH_SIZE + hash_value];
+
+               mf->hash[hash_2_value] = pos;
+               mf->hash[FIX_3_HASH_SIZE + hash_3_value] = pos;
+               mf->hash[FIX_4_HASH_SIZE + hash_value] = pos;
+
+               hc_skip();
+
+       } while (--amount != 0);
+}
+#endif
+
+
+/////////////////
+// Binary Tree //
+/////////////////
+
+#if defined(HAVE_MF_BT2) || defined(HAVE_MF_BT3) || defined(HAVE_MF_BT4)
+static lzma_match *
+bt_find_func(
+               const uint32_t len_limit,
+               const uint32_t pos,
+               const uint8_t *const cur,
+               uint32_t cur_match,
+               uint32_t depth,
+               uint32_t *const son,
+               const uint32_t cyclic_pos,
+               const uint32_t cyclic_size,
+               lzma_match *matches,
+               uint32_t len_best)
+{
+       uint32_t *ptr0 = son + (cyclic_pos << 1) + 1;
+       uint32_t *ptr1 = son + (cyclic_pos << 1);
+
+       uint32_t len0 = 0;
+       uint32_t len1 = 0;
+
+       while (true) {
+               const uint32_t delta = pos - cur_match;
+               if (depth-- == 0 || delta >= cyclic_size) {
+                       *ptr0 = EMPTY_HASH_VALUE;
+                       *ptr1 = EMPTY_HASH_VALUE;
+                       return matches;
+               }
+
+               uint32_t *const pair = son + ((cyclic_pos - delta
+                               + (delta > cyclic_pos ? cyclic_size : 0))
+                               << 1);
+
+               const uint8_t *const pb = cur - delta;
+               uint32_t len = my_min(len0, len1);
+
+               if (pb[len] == cur[len]) {
+                       len = lzma_memcmplen(pb, cur, len + 1, len_limit);
+
+                       if (len_best < len) {
+                               len_best = len;
+                               matches->len = len;
+                               matches->dist = delta - 1;
+                               ++matches;
+
+                               if (len == len_limit) {
+                                       *ptr1 = pair[0];
+                                       *ptr0 = pair[1];
+                                       return matches;
+                               }
+                       }
+               }
+
+               if (pb[len] < cur[len]) {
+                       *ptr1 = cur_match;
+                       ptr1 = pair + 1;
+                       cur_match = *ptr1;
+                       len1 = len;
+               } else {
+                       *ptr0 = cur_match;
+                       ptr0 = pair;
+                       cur_match = *ptr0;
+                       len0 = len;
+               }
+       }
+}
+
+
+static void
+bt_skip_func(
+               const uint32_t len_limit,
+               const uint32_t pos,
+               const uint8_t *const cur,
+               uint32_t cur_match,
+               uint32_t depth,
+               uint32_t *const son,
+               const uint32_t cyclic_pos,
+               const uint32_t cyclic_size)
+{
+       uint32_t *ptr0 = son + (cyclic_pos << 1) + 1;
+       uint32_t *ptr1 = son + (cyclic_pos << 1);
+
+       uint32_t len0 = 0;
+       uint32_t len1 = 0;
+
+       while (true) {
+               const uint32_t delta = pos - cur_match;
+               if (depth-- == 0 || delta >= cyclic_size) {
+                       *ptr0 = EMPTY_HASH_VALUE;
+                       *ptr1 = EMPTY_HASH_VALUE;
+                       return;
+               }
+
+               uint32_t *pair = son + ((cyclic_pos - delta
+                               + (delta > cyclic_pos ? cyclic_size : 0))
+                               << 1);
+               const uint8_t *pb = cur - delta;
+               uint32_t len = my_min(len0, len1);
+
+               if (pb[len] == cur[len]) {
+                       len = lzma_memcmplen(pb, cur, len + 1, len_limit);
+
+                       if (len == len_limit) {
+                               *ptr1 = pair[0];
+                               *ptr0 = pair[1];
+                               return;
+                       }
+               }
+
+               if (pb[len] < cur[len]) {
+                       *ptr1 = cur_match;
+                       ptr1 = pair + 1;
+                       cur_match = *ptr1;
+                       len1 = len;
+               } else {
+                       *ptr0 = cur_match;
+                       ptr0 = pair;
+                       cur_match = *ptr0;
+                       len0 = len;
+               }
+       }
+}
+
+
+#define bt_find(len_best) \
+       call_find(bt_find_func, len_best)
+
+#define bt_skip() \
+do { \
+       bt_skip_func(len_limit, pos, cur, cur_match, mf->depth, \
+                       mf->son, mf->cyclic_pos, \
+                       mf->cyclic_size); \
+       move_pos(mf); \
+} while (0)
+
+#endif
+
+
+#ifdef HAVE_MF_BT2
+extern uint32_t
+lzma_mf_bt2_find(lzma_mf *mf, lzma_match *matches)
+{
+       header_find(true, 2);
+
+       hash_2_calc();
+
+       const uint32_t cur_match = mf->hash[hash_value];
+       mf->hash[hash_value] = pos;
+
+       bt_find(1);
+}
+
+
+extern void
+lzma_mf_bt2_skip(lzma_mf *mf, uint32_t amount)
+{
+       do {
+               header_skip(true, 2);
+
+               hash_2_calc();
+
+               const uint32_t cur_match = mf->hash[hash_value];
+               mf->hash[hash_value] = pos;
+
+               bt_skip();
+
+       } while (--amount != 0);
+}
+#endif
+
+
+#ifdef HAVE_MF_BT3
+extern uint32_t
+lzma_mf_bt3_find(lzma_mf *mf, lzma_match *matches)
+{
+       header_find(true, 3);
+
+       hash_3_calc();
+
+       const uint32_t delta2 = pos - mf->hash[hash_2_value];
+       const uint32_t cur_match = mf->hash[FIX_3_HASH_SIZE + hash_value];
+
+       mf->hash[hash_2_value] = pos;
+       mf->hash[FIX_3_HASH_SIZE + hash_value] = pos;
+
+       uint32_t len_best = 2;
+
+       if (delta2 < mf->cyclic_size && *(cur - delta2) == *cur) {
+               len_best = lzma_memcmplen(
+                               cur, cur - delta2, len_best, len_limit);
+
+               matches[0].len = len_best;
+               matches[0].dist = delta2 - 1;
+               matches_count = 1;
+
+               if (len_best == len_limit) {
+                       bt_skip();
+                       return 1; // matches_count
+               }
+       }
+
+       bt_find(len_best);
+}
+
+
+extern void
+lzma_mf_bt3_skip(lzma_mf *mf, uint32_t amount)
+{
+       do {
+               header_skip(true, 3);
+
+               hash_3_calc();
+
+               const uint32_t cur_match
+                               = mf->hash[FIX_3_HASH_SIZE + hash_value];
+
+               mf->hash[hash_2_value] = pos;
+               mf->hash[FIX_3_HASH_SIZE + hash_value] = pos;
+
+               bt_skip();
+
+       } while (--amount != 0);
+}
+#endif
+
+
+#ifdef HAVE_MF_BT4
+extern uint32_t
+lzma_mf_bt4_find(lzma_mf *mf, lzma_match *matches)
+{
+       header_find(true, 4);
+
+       hash_4_calc();
+
+       uint32_t delta2 = pos - mf->hash[hash_2_value];
+       const uint32_t delta3
+                       = pos - mf->hash[FIX_3_HASH_SIZE + hash_3_value];
+       const uint32_t cur_match = mf->hash[FIX_4_HASH_SIZE + hash_value];
+
+       mf->hash[hash_2_value] = pos;
+       mf->hash[FIX_3_HASH_SIZE + hash_3_value] = pos;
+       mf->hash[FIX_4_HASH_SIZE + hash_value] = pos;
+
+       uint32_t len_best = 1;
+
+       if (delta2 < mf->cyclic_size && *(cur - delta2) == *cur) {
+               len_best = 2;
+               matches[0].len = 2;
+               matches[0].dist = delta2 - 1;
+               matches_count = 1;
+       }
+
+       if (delta2 != delta3 && delta3 < mf->cyclic_size
+                       && *(cur - delta3) == *cur) {
+               len_best = 3;
+               matches[matches_count++].dist = delta3 - 1;
+               delta2 = delta3;
+       }
+
+       if (matches_count != 0) {
+               len_best = lzma_memcmplen(
+                               cur, cur - delta2, len_best, len_limit);
+
+               matches[matches_count - 1].len = len_best;
+
+               if (len_best == len_limit) {
+                       bt_skip();
+                       return matches_count;
+               }
+       }
+
+       if (len_best < 3)
+               len_best = 3;
+
+       bt_find(len_best);
+}
+
+
+extern void
+lzma_mf_bt4_skip(lzma_mf *mf, uint32_t amount)
+{
+       do {
+               header_skip(true, 4);
+
+               hash_4_calc();
+
+               const uint32_t cur_match
+                               = mf->hash[FIX_4_HASH_SIZE + hash_value];
+
+               mf->hash[hash_2_value] = pos;
+               mf->hash[FIX_3_HASH_SIZE + hash_3_value] = pos;
+               mf->hash[FIX_4_HASH_SIZE + hash_value] = pos;
+
+               bt_skip();
+
+       } while (--amount != 0);
+}
+#endif