Path: blob/master/Utilities/cmliblzma/liblzma/lz/lz_encoder_mf.c
3156 views
// SPDX-License-Identifier: 0BSD12///////////////////////////////////////////////////////////////////////////////3//4/// \file lz_encoder_mf.c5/// \brief Match finders6///7// Authors: Igor Pavlov8// Lasse Collin9//10///////////////////////////////////////////////////////////////////////////////1112#include "lz_encoder.h"13#include "lz_encoder_hash.h"14#include "memcmplen.h"151617/// \brief Find matches starting from the current byte18///19/// \return The length of the longest match found20extern uint32_t21lzma_mf_find(lzma_mf *mf, uint32_t *count_ptr, lzma_match *matches)22{23// Call the match finder. It returns the number of length-distance24// pairs found.25// FIXME: Minimum count is zero, what _exactly_ is the maximum?26const uint32_t count = mf->find(mf, matches);2728// Length of the longest match; assume that no matches were found29// and thus the maximum length is zero.30uint32_t len_best = 0;3132if (count > 0) {33#ifndef NDEBUG34// Validate the matches.35for (uint32_t i = 0; i < count; ++i) {36assert(matches[i].len <= mf->nice_len);37assert(matches[i].dist < mf->read_pos);38assert(memcmp(mf_ptr(mf) - 1,39mf_ptr(mf) - matches[i].dist - 2,40matches[i].len) == 0);41}42#endif4344// The last used element in the array contains45// the longest match.46len_best = matches[count - 1].len;4748// If a match of maximum search length was found, try to49// extend the match to maximum possible length.50if (len_best == mf->nice_len) {51// The limit for the match length is either the52// maximum match length supported by the LZ-based53// encoder or the number of bytes left in the54// dictionary, whichever is smaller.55uint32_t limit = mf_avail(mf) + 1;56if (limit > mf->match_len_max)57limit = mf->match_len_max;5859// Pointer to the byte we just ran through60// the match finder.61const uint8_t *p1 = mf_ptr(mf) - 1;6263// Pointer to the beginning of the match. We need -164// here because the match distances are zero based.65const uint8_t *p2 = p1 - matches[count - 1].dist - 1;6667len_best = lzma_memcmplen(p1, p2, len_best, limit);68}69}7071*count_ptr = count;7273// Finally update the read position to indicate that match finder was74// run for this dictionary offset.75++mf->read_ahead;7677return len_best;78}798081/// Hash value to indicate unused element in the hash. Since we start the82/// positions from dict_size + 1, zero is always too far to qualify83/// as usable match position.84#define EMPTY_HASH_VALUE 0858687/// Normalization must be done when lzma_mf.offset + lzma_mf.read_pos88/// reaches MUST_NORMALIZE_POS.89#define MUST_NORMALIZE_POS UINT32_MAX909192/// \brief Normalizes hash values93///94/// The hash arrays store positions of match candidates. The positions are95/// relative to an arbitrary offset that is not the same as the absolute96/// offset in the input stream. The relative position of the current byte97/// is lzma_mf.offset + lzma_mf.read_pos. The distances of the matches are98/// the differences of the current read position and the position found from99/// the hash.100///101/// To prevent integer overflows of the offsets stored in the hash arrays,102/// we need to "normalize" the stored values now and then. During the103/// normalization, we drop values that indicate distance greater than the104/// dictionary size, thus making space for new values.105static void106normalize(lzma_mf *mf)107{108assert(mf->read_pos + mf->offset == MUST_NORMALIZE_POS);109110// In future we may not want to touch the lowest bits, because there111// may be match finders that use larger resolution than one byte.112const uint32_t subvalue113= (MUST_NORMALIZE_POS - mf->cyclic_size);114// & ~((UINT32_C(1) << 10) - 1);115116for (uint32_t i = 0; i < mf->hash_count; ++i) {117// If the distance is greater than the dictionary size,118// we can simply mark the hash element as empty.119if (mf->hash[i] <= subvalue)120mf->hash[i] = EMPTY_HASH_VALUE;121else122mf->hash[i] -= subvalue;123}124125for (uint32_t i = 0; i < mf->sons_count; ++i) {126// Do the same for mf->son.127//128// NOTE: There may be uninitialized elements in mf->son.129// Valgrind may complain that the "if" below depends on130// an uninitialized value. In this case it is safe to ignore131// the warning. See also the comments in lz_encoder_init()132// in lz_encoder.c.133if (mf->son[i] <= subvalue)134mf->son[i] = EMPTY_HASH_VALUE;135else136mf->son[i] -= subvalue;137}138139// Update offset to match the new locations.140mf->offset -= subvalue;141142return;143}144145146/// Mark the current byte as processed from point of view of the match finder.147static void148move_pos(lzma_mf *mf)149{150if (++mf->cyclic_pos == mf->cyclic_size)151mf->cyclic_pos = 0;152153++mf->read_pos;154assert(mf->read_pos <= mf->write_pos);155156if (unlikely(mf->read_pos + mf->offset == UINT32_MAX))157normalize(mf);158}159160161/// When flushing, we cannot run the match finder unless there is nice_len162/// bytes available in the dictionary. Instead, we skip running the match163/// finder (indicating that no match was found), and count how many bytes we164/// have ignored this way.165///166/// When new data is given after the flushing was completed, the match finder167/// is restarted by rewinding mf->read_pos backwards by mf->pending. Then168/// the missed bytes are added to the hash using the match finder's skip169/// function (with small amount of input, it may start using mf->pending170/// again if flushing).171///172/// Due to this rewinding, we don't touch cyclic_pos or test for173/// normalization. It will be done when the match finder's skip function174/// catches up after a flush.175static void176move_pending(lzma_mf *mf)177{178++mf->read_pos;179assert(mf->read_pos <= mf->write_pos);180++mf->pending;181}182183184/// Calculate len_limit and determine if there is enough input to run185/// the actual match finder code. Sets up "cur" and "pos". This macro186/// is used by all find functions and binary tree skip functions. Hash187/// chain skip function doesn't need len_limit so a simpler code is used188/// in them.189#define header(is_bt, len_min, ret_op) \190uint32_t len_limit = mf_avail(mf); \191if (mf->nice_len <= len_limit) { \192len_limit = mf->nice_len; \193} else if (len_limit < (len_min) \194|| (is_bt && mf->action == LZMA_SYNC_FLUSH)) { \195assert(mf->action != LZMA_RUN); \196move_pending(mf); \197ret_op; \198} \199const uint8_t *cur = mf_ptr(mf); \200const uint32_t pos = mf->read_pos + mf->offset201202203/// Header for find functions. "return 0" indicates that zero matches204/// were found.205#define header_find(is_bt, len_min) \206header(is_bt, len_min, return 0); \207uint32_t matches_count = 0208209210/// Header for a loop in a skip function. "continue" tells to skip the rest211/// of the code in the loop.212#define header_skip(is_bt, len_min) \213header(is_bt, len_min, continue)214215216/// Calls hc_find_func() or bt_find_func() and calculates the total number217/// of matches found. Updates the dictionary position and returns the number218/// of matches found.219#define call_find(func, len_best) \220do { \221matches_count = (uint32_t)(func(len_limit, pos, cur, cur_match, \222mf->depth, mf->son, \223mf->cyclic_pos, mf->cyclic_size, \224matches + matches_count, len_best) \225- matches); \226move_pos(mf); \227return matches_count; \228} while (0)229230231////////////////232// Hash Chain //233////////////////234235#if defined(HAVE_MF_HC3) || defined(HAVE_MF_HC4)236///237///238/// \param len_limit Don't look for matches longer than len_limit.239/// \param pos lzma_mf.read_pos + lzma_mf.offset240/// \param cur Pointer to current byte (mf_ptr(mf))241/// \param cur_match Start position of the current match candidate242/// \param depth Maximum length of the hash chain243/// \param son lzma_mf.son (contains the hash chain)244/// \param cyclic_pos lzma_mf.cyclic_pos245/// \param cyclic_size lzma_mf_cyclic_size246/// \param matches Array to hold the matches.247/// \param len_best The length of the longest match found so far.248static lzma_match *249hc_find_func(250const uint32_t len_limit,251const uint32_t pos,252const uint8_t *const cur,253uint32_t cur_match,254uint32_t depth,255uint32_t *const son,256const uint32_t cyclic_pos,257const uint32_t cyclic_size,258lzma_match *matches,259uint32_t len_best)260{261son[cyclic_pos] = cur_match;262263while (true) {264const uint32_t delta = pos - cur_match;265if (depth-- == 0 || delta >= cyclic_size)266return matches;267268const uint8_t *const pb = cur - delta;269cur_match = son[cyclic_pos - delta270+ (delta > cyclic_pos ? cyclic_size : 0)];271272if (pb[len_best] == cur[len_best] && pb[0] == cur[0]) {273uint32_t len = lzma_memcmplen(pb, cur, 1, len_limit);274275if (len_best < len) {276len_best = len;277matches->len = len;278matches->dist = delta - 1;279++matches;280281if (len == len_limit)282return matches;283}284}285}286}287288289#define hc_find(len_best) \290call_find(hc_find_func, len_best)291292293#define hc_skip() \294do { \295mf->son[mf->cyclic_pos] = cur_match; \296move_pos(mf); \297} while (0)298299#endif300301302#ifdef HAVE_MF_HC3303extern uint32_t304lzma_mf_hc3_find(lzma_mf *mf, lzma_match *matches)305{306header_find(false, 3);307308hash_3_calc();309310const uint32_t delta2 = pos - mf->hash[hash_2_value];311const uint32_t cur_match = mf->hash[FIX_3_HASH_SIZE + hash_value];312313mf->hash[hash_2_value] = pos;314mf->hash[FIX_3_HASH_SIZE + hash_value] = pos;315316uint32_t len_best = 2;317318if (delta2 < mf->cyclic_size && *(cur - delta2) == *cur) {319len_best = lzma_memcmplen(cur - delta2, cur,320len_best, len_limit);321322matches[0].len = len_best;323matches[0].dist = delta2 - 1;324matches_count = 1;325326if (len_best == len_limit) {327hc_skip();328return 1; // matches_count329}330}331332hc_find(len_best);333}334335336extern void337lzma_mf_hc3_skip(lzma_mf *mf, uint32_t amount)338{339do {340if (mf_avail(mf) < 3) {341move_pending(mf);342continue;343}344345const uint8_t *cur = mf_ptr(mf);346const uint32_t pos = mf->read_pos + mf->offset;347348hash_3_calc();349350const uint32_t cur_match351= mf->hash[FIX_3_HASH_SIZE + hash_value];352353mf->hash[hash_2_value] = pos;354mf->hash[FIX_3_HASH_SIZE + hash_value] = pos;355356hc_skip();357358} while (--amount != 0);359}360#endif361362363#ifdef HAVE_MF_HC4364extern uint32_t365lzma_mf_hc4_find(lzma_mf *mf, lzma_match *matches)366{367header_find(false, 4);368369hash_4_calc();370371uint32_t delta2 = pos - mf->hash[hash_2_value];372const uint32_t delta3373= pos - mf->hash[FIX_3_HASH_SIZE + hash_3_value];374const uint32_t cur_match = mf->hash[FIX_4_HASH_SIZE + hash_value];375376mf->hash[hash_2_value ] = pos;377mf->hash[FIX_3_HASH_SIZE + hash_3_value] = pos;378mf->hash[FIX_4_HASH_SIZE + hash_value] = pos;379380uint32_t len_best = 1;381382if (delta2 < mf->cyclic_size && *(cur - delta2) == *cur) {383len_best = 2;384matches[0].len = 2;385matches[0].dist = delta2 - 1;386matches_count = 1;387}388389if (delta2 != delta3 && delta3 < mf->cyclic_size390&& *(cur - delta3) == *cur) {391len_best = 3;392matches[matches_count++].dist = delta3 - 1;393delta2 = delta3;394}395396if (matches_count != 0) {397len_best = lzma_memcmplen(cur - delta2, cur,398len_best, len_limit);399400matches[matches_count - 1].len = len_best;401402if (len_best == len_limit) {403hc_skip();404return matches_count;405}406}407408if (len_best < 3)409len_best = 3;410411hc_find(len_best);412}413414415extern void416lzma_mf_hc4_skip(lzma_mf *mf, uint32_t amount)417{418do {419if (mf_avail(mf) < 4) {420move_pending(mf);421continue;422}423424const uint8_t *cur = mf_ptr(mf);425const uint32_t pos = mf->read_pos + mf->offset;426427hash_4_calc();428429const uint32_t cur_match430= mf->hash[FIX_4_HASH_SIZE + hash_value];431432mf->hash[hash_2_value] = pos;433mf->hash[FIX_3_HASH_SIZE + hash_3_value] = pos;434mf->hash[FIX_4_HASH_SIZE + hash_value] = pos;435436hc_skip();437438} while (--amount != 0);439}440#endif441442443/////////////////444// Binary Tree //445/////////////////446447#if defined(HAVE_MF_BT2) || defined(HAVE_MF_BT3) || defined(HAVE_MF_BT4)448static lzma_match *449bt_find_func(450const uint32_t len_limit,451const uint32_t pos,452const uint8_t *const cur,453uint32_t cur_match,454uint32_t depth,455uint32_t *const son,456const uint32_t cyclic_pos,457const uint32_t cyclic_size,458lzma_match *matches,459uint32_t len_best)460{461uint32_t *ptr0 = son + (cyclic_pos << 1) + 1;462uint32_t *ptr1 = son + (cyclic_pos << 1);463464uint32_t len0 = 0;465uint32_t len1 = 0;466467while (true) {468const uint32_t delta = pos - cur_match;469if (depth-- == 0 || delta >= cyclic_size) {470*ptr0 = EMPTY_HASH_VALUE;471*ptr1 = EMPTY_HASH_VALUE;472return matches;473}474475uint32_t *const pair = son + ((cyclic_pos - delta476+ (delta > cyclic_pos ? cyclic_size : 0))477<< 1);478479const uint8_t *const pb = cur - delta;480uint32_t len = my_min(len0, len1);481482if (pb[len] == cur[len]) {483len = lzma_memcmplen(pb, cur, len + 1, len_limit);484485if (len_best < len) {486len_best = len;487matches->len = len;488matches->dist = delta - 1;489++matches;490491if (len == len_limit) {492*ptr1 = pair[0];493*ptr0 = pair[1];494return matches;495}496}497}498499if (pb[len] < cur[len]) {500*ptr1 = cur_match;501ptr1 = pair + 1;502cur_match = *ptr1;503len1 = len;504} else {505*ptr0 = cur_match;506ptr0 = pair;507cur_match = *ptr0;508len0 = len;509}510}511}512513514static void515bt_skip_func(516const uint32_t len_limit,517const uint32_t pos,518const uint8_t *const cur,519uint32_t cur_match,520uint32_t depth,521uint32_t *const son,522const uint32_t cyclic_pos,523const uint32_t cyclic_size)524{525uint32_t *ptr0 = son + (cyclic_pos << 1) + 1;526uint32_t *ptr1 = son + (cyclic_pos << 1);527528uint32_t len0 = 0;529uint32_t len1 = 0;530531while (true) {532const uint32_t delta = pos - cur_match;533if (depth-- == 0 || delta >= cyclic_size) {534*ptr0 = EMPTY_HASH_VALUE;535*ptr1 = EMPTY_HASH_VALUE;536return;537}538539uint32_t *pair = son + ((cyclic_pos - delta540+ (delta > cyclic_pos ? cyclic_size : 0))541<< 1);542const uint8_t *pb = cur - delta;543uint32_t len = my_min(len0, len1);544545if (pb[len] == cur[len]) {546len = lzma_memcmplen(pb, cur, len + 1, len_limit);547548if (len == len_limit) {549*ptr1 = pair[0];550*ptr0 = pair[1];551return;552}553}554555if (pb[len] < cur[len]) {556*ptr1 = cur_match;557ptr1 = pair + 1;558cur_match = *ptr1;559len1 = len;560} else {561*ptr0 = cur_match;562ptr0 = pair;563cur_match = *ptr0;564len0 = len;565}566}567}568569570#define bt_find(len_best) \571call_find(bt_find_func, len_best)572573#define bt_skip() \574do { \575bt_skip_func(len_limit, pos, cur, cur_match, mf->depth, \576mf->son, mf->cyclic_pos, \577mf->cyclic_size); \578move_pos(mf); \579} while (0)580581#endif582583584#ifdef HAVE_MF_BT2585extern uint32_t586lzma_mf_bt2_find(lzma_mf *mf, lzma_match *matches)587{588header_find(true, 2);589590hash_2_calc();591592const uint32_t cur_match = mf->hash[hash_value];593mf->hash[hash_value] = pos;594595bt_find(1);596}597598599extern void600lzma_mf_bt2_skip(lzma_mf *mf, uint32_t amount)601{602do {603header_skip(true, 2);604605hash_2_calc();606607const uint32_t cur_match = mf->hash[hash_value];608mf->hash[hash_value] = pos;609610bt_skip();611612} while (--amount != 0);613}614#endif615616617#ifdef HAVE_MF_BT3618extern uint32_t619lzma_mf_bt3_find(lzma_mf *mf, lzma_match *matches)620{621header_find(true, 3);622623hash_3_calc();624625const uint32_t delta2 = pos - mf->hash[hash_2_value];626const uint32_t cur_match = mf->hash[FIX_3_HASH_SIZE + hash_value];627628mf->hash[hash_2_value] = pos;629mf->hash[FIX_3_HASH_SIZE + hash_value] = pos;630631uint32_t len_best = 2;632633if (delta2 < mf->cyclic_size && *(cur - delta2) == *cur) {634len_best = lzma_memcmplen(635cur, cur - delta2, len_best, len_limit);636637matches[0].len = len_best;638matches[0].dist = delta2 - 1;639matches_count = 1;640641if (len_best == len_limit) {642bt_skip();643return 1; // matches_count644}645}646647bt_find(len_best);648}649650651extern void652lzma_mf_bt3_skip(lzma_mf *mf, uint32_t amount)653{654do {655header_skip(true, 3);656657hash_3_calc();658659const uint32_t cur_match660= mf->hash[FIX_3_HASH_SIZE + hash_value];661662mf->hash[hash_2_value] = pos;663mf->hash[FIX_3_HASH_SIZE + hash_value] = pos;664665bt_skip();666667} while (--amount != 0);668}669#endif670671672#ifdef HAVE_MF_BT4673extern uint32_t674lzma_mf_bt4_find(lzma_mf *mf, lzma_match *matches)675{676header_find(true, 4);677678hash_4_calc();679680uint32_t delta2 = pos - mf->hash[hash_2_value];681const uint32_t delta3682= pos - mf->hash[FIX_3_HASH_SIZE + hash_3_value];683const uint32_t cur_match = mf->hash[FIX_4_HASH_SIZE + hash_value];684685mf->hash[hash_2_value] = pos;686mf->hash[FIX_3_HASH_SIZE + hash_3_value] = pos;687mf->hash[FIX_4_HASH_SIZE + hash_value] = pos;688689uint32_t len_best = 1;690691if (delta2 < mf->cyclic_size && *(cur - delta2) == *cur) {692len_best = 2;693matches[0].len = 2;694matches[0].dist = delta2 - 1;695matches_count = 1;696}697698if (delta2 != delta3 && delta3 < mf->cyclic_size699&& *(cur - delta3) == *cur) {700len_best = 3;701matches[matches_count++].dist = delta3 - 1;702delta2 = delta3;703}704705if (matches_count != 0) {706len_best = lzma_memcmplen(707cur, cur - delta2, len_best, len_limit);708709matches[matches_count - 1].len = len_best;710711if (len_best == len_limit) {712bt_skip();713return matches_count;714}715}716717if (len_best < 3)718len_best = 3;719720bt_find(len_best);721}722723724extern void725lzma_mf_bt4_skip(lzma_mf *mf, uint32_t amount)726{727do {728header_skip(true, 4);729730hash_4_calc();731732const uint32_t cur_match733= mf->hash[FIX_4_HASH_SIZE + hash_value];734735mf->hash[hash_2_value] = pos;736mf->hash[FIX_3_HASH_SIZE + hash_3_value] = pos;737mf->hash[FIX_4_HASH_SIZE + hash_value] = pos;738739bt_skip();740741} while (--amount != 0);742}743#endif744745746