Rev 49813 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
//////////////////////////////////////////////////////////////////////////////////// \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 "check.h"/// \brief Find matches starting from the current byte////// \return The length of the longest match foundextern uint32_tlzma_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;while (len_best < limit&& p1[len_best] == p2[len_best])++len_best;}}*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 voidnormalize(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);const uint32_t count = mf->hash_size_sum + mf->sons_count;uint32_t *hash = mf->hash;for (uint32_t i = 0; i < count; ++i) {// If the distance is greater than the dictionary size,// we can simply mark the hash element as empty.//// NOTE: Only the first mf->hash_size_sum elements are// initialized for sure. There may be uninitialized elements// in mf->son. Since we go through both mf->hash and// mf->son here in normalization, Valgrind may complain// that the "if" below depends on 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 (hash[i] <= subvalue)hash[i] = EMPTY_HASH_VALUE;elsehash[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 voidmove_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 voidmove_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 = 0;while (++len != len_limit)if (pb[len] != cur[len])break;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_HC3extern uint32_tlzma_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) {for ( ; len_best != len_limit; ++len_best)if (*(cur + len_best - delta2) != cur[len_best])break;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 voidlzma_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_HC4extern uint32_tlzma_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) {for ( ; len_best != len_limit; ++len_best)if (*(cur + len_best - delta2) != cur[len_best])break;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 voidlzma_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 = MIN(len0, len1);if (pb[len] == cur[len]) {while (++len != len_limit)if (pb[len] != cur[len])break;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 voidbt_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 = MIN(len0, len1);if (pb[len] == cur[len]) {while (++len != len_limit)if (pb[len] != cur[len])break;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_BT2extern uint32_tlzma_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 voidlzma_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_BT3extern uint32_tlzma_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) {for ( ; len_best != len_limit; ++len_best)if (*(cur + len_best - delta2) != cur[len_best])break;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 voidlzma_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_BT4extern uint32_tlzma_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) {for ( ; len_best != len_limit; ++len_best)if (*(cur + len_best - delta2) != cur[len_best])break;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 voidlzma_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