123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744 |
- // SPDX-License-Identifier: 0BSD
- ///////////////////////////////////////////////////////////////////////////////
- //
- /// \file lz_encoder_mf.c
- /// \brief Match finders
- ///
- // Authors: Igor Pavlov
- // Lasse Collin
- //
- ///////////////////////////////////////////////////////////////////////////////
- #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 = (uint32_t)(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 lzma_mf.cyclic_pos
- /// \param cyclic_size lzma_mf_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
|