Home | History | Annotate | Download | only in C
      1 /* LzFindMt.c -- multithreaded Match finder for LZ algorithms
      2 2009-09-20 : Igor Pavlov : Public domain */
      3 
      4 #include "LzHash.h"
      5 
      6 #include "LzFindMt.h"
      7 
      8 void MtSync_Construct(CMtSync *p)
      9 {
     10   p->wasCreated = False;
     11   p->csWasInitialized = False;
     12   p->csWasEntered = False;
     13   Thread_Construct(&p->thread);
     14   Event_Construct(&p->canStart);
     15   Event_Construct(&p->wasStarted);
     16   Event_Construct(&p->wasStopped);
     17   Semaphore_Construct(&p->freeSemaphore);
     18   Semaphore_Construct(&p->filledSemaphore);
     19 }
     20 
     21 void MtSync_GetNextBlock(CMtSync *p)
     22 {
     23   if (p->needStart)
     24   {
     25     p->numProcessedBlocks = 1;
     26     p->needStart = False;
     27     p->stopWriting = False;
     28     p->exit = False;
     29     Event_Reset(&p->wasStarted);
     30     Event_Reset(&p->wasStopped);
     31 
     32     Event_Set(&p->canStart);
     33     Event_Wait(&p->wasStarted);
     34   }
     35   else
     36   {
     37     CriticalSection_Leave(&p->cs);
     38     p->csWasEntered = False;
     39     p->numProcessedBlocks++;
     40     Semaphore_Release1(&p->freeSemaphore);
     41   }
     42   Semaphore_Wait(&p->filledSemaphore);
     43   CriticalSection_Enter(&p->cs);
     44   p->csWasEntered = True;
     45 }
     46 
     47 /* MtSync_StopWriting must be called if Writing was started */
     48 
     49 void MtSync_StopWriting(CMtSync *p)
     50 {
     51   UInt32 myNumBlocks = p->numProcessedBlocks;
     52   if (!Thread_WasCreated(&p->thread) || p->needStart)
     53     return;
     54   p->stopWriting = True;
     55   if (p->csWasEntered)
     56   {
     57     CriticalSection_Leave(&p->cs);
     58     p->csWasEntered = False;
     59   }
     60   Semaphore_Release1(&p->freeSemaphore);
     61 
     62   Event_Wait(&p->wasStopped);
     63 
     64   while (myNumBlocks++ != p->numProcessedBlocks)
     65   {
     66     Semaphore_Wait(&p->filledSemaphore);
     67     Semaphore_Release1(&p->freeSemaphore);
     68   }
     69   p->needStart = True;
     70 }
     71 
     72 void MtSync_Destruct(CMtSync *p)
     73 {
     74   if (Thread_WasCreated(&p->thread))
     75   {
     76     MtSync_StopWriting(p);
     77     p->exit = True;
     78     if (p->needStart)
     79       Event_Set(&p->canStart);
     80     Thread_Wait(&p->thread);
     81     Thread_Close(&p->thread);
     82   }
     83   if (p->csWasInitialized)
     84   {
     85     CriticalSection_Delete(&p->cs);
     86     p->csWasInitialized = False;
     87   }
     88 
     89   Event_Close(&p->canStart);
     90   Event_Close(&p->wasStarted);
     91   Event_Close(&p->wasStopped);
     92   Semaphore_Close(&p->freeSemaphore);
     93   Semaphore_Close(&p->filledSemaphore);
     94 
     95   p->wasCreated = False;
     96 }
     97 
     98 #define RINOK_THREAD(x) { if ((x) != 0) return SZ_ERROR_THREAD; }
     99 
    100 static SRes MtSync_Create2(CMtSync *p, unsigned (MY_STD_CALL *startAddress)(void *), void *obj, UInt32 numBlocks)
    101 {
    102   if (p->wasCreated)
    103     return SZ_OK;
    104 
    105   RINOK_THREAD(CriticalSection_Init(&p->cs));
    106   p->csWasInitialized = True;
    107 
    108   RINOK_THREAD(AutoResetEvent_CreateNotSignaled(&p->canStart));
    109   RINOK_THREAD(AutoResetEvent_CreateNotSignaled(&p->wasStarted));
    110   RINOK_THREAD(AutoResetEvent_CreateNotSignaled(&p->wasStopped));
    111 
    112   RINOK_THREAD(Semaphore_Create(&p->freeSemaphore, numBlocks, numBlocks));
    113   RINOK_THREAD(Semaphore_Create(&p->filledSemaphore, 0, numBlocks));
    114 
    115   p->needStart = True;
    116 
    117   RINOK_THREAD(Thread_Create(&p->thread, startAddress, obj));
    118   p->wasCreated = True;
    119   return SZ_OK;
    120 }
    121 
    122 static SRes MtSync_Create(CMtSync *p, unsigned (MY_STD_CALL *startAddress)(void *), void *obj, UInt32 numBlocks)
    123 {
    124   SRes res = MtSync_Create2(p, startAddress, obj, numBlocks);
    125   if (res != SZ_OK)
    126     MtSync_Destruct(p);
    127   return res;
    128 }
    129 
    130 void MtSync_Init(CMtSync *p) { p->needStart = True; }
    131 
    132 #define kMtMaxValForNormalize 0xFFFFFFFF
    133 
    134 #define DEF_GetHeads2(name, v, action) \
    135 static void GetHeads ## name(const Byte *p, UInt32 pos, \
    136 UInt32 *hash, UInt32 hashMask, UInt32 *heads, UInt32 numHeads, const UInt32 *crc) \
    137 { action; for (; numHeads != 0; numHeads--) { \
    138 const UInt32 value = (v); p++; *heads++ = pos - hash[value]; hash[value] = pos++;  } }
    139 
    140 #define DEF_GetHeads(name, v) DEF_GetHeads2(name, v, ;)
    141 
    142 DEF_GetHeads2(2,  (p[0] | ((UInt32)p[1] << 8)), hashMask = hashMask; crc = crc; )
    143 DEF_GetHeads(3,  (crc[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8)) & hashMask)
    144 DEF_GetHeads(4,  (crc[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8) ^ (crc[p[3]] << 5)) & hashMask)
    145 DEF_GetHeads(4b, (crc[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8) ^ ((UInt32)p[3] << 16)) & hashMask)
    146 /* DEF_GetHeads(5,  (crc[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8) ^ (crc[p[3]] << 5) ^ (crc[p[4]] << 3)) & hashMask) */
    147 
    148 void HashThreadFunc(CMatchFinderMt *mt)
    149 {
    150   CMtSync *p = &mt->hashSync;
    151   for (;;)
    152   {
    153     UInt32 numProcessedBlocks = 0;
    154     Event_Wait(&p->canStart);
    155     Event_Set(&p->wasStarted);
    156     for (;;)
    157     {
    158       if (p->exit)
    159         return;
    160       if (p->stopWriting)
    161       {
    162         p->numProcessedBlocks = numProcessedBlocks;
    163         Event_Set(&p->wasStopped);
    164         break;
    165       }
    166 
    167       {
    168         CMatchFinder *mf = mt->MatchFinder;
    169         if (MatchFinder_NeedMove(mf))
    170         {
    171           CriticalSection_Enter(&mt->btSync.cs);
    172           CriticalSection_Enter(&mt->hashSync.cs);
    173           {
    174             const Byte *beforePtr = MatchFinder_GetPointerToCurrentPos(mf);
    175             const Byte *afterPtr;
    176             MatchFinder_MoveBlock(mf);
    177             afterPtr = MatchFinder_GetPointerToCurrentPos(mf);
    178             mt->pointerToCurPos -= beforePtr - afterPtr;
    179             mt->buffer -= beforePtr - afterPtr;
    180           }
    181           CriticalSection_Leave(&mt->btSync.cs);
    182           CriticalSection_Leave(&mt->hashSync.cs);
    183           continue;
    184         }
    185 
    186         Semaphore_Wait(&p->freeSemaphore);
    187 
    188         MatchFinder_ReadIfRequired(mf);
    189         if (mf->pos > (kMtMaxValForNormalize - kMtHashBlockSize))
    190         {
    191           UInt32 subValue = (mf->pos - mf->historySize - 1);
    192           MatchFinder_ReduceOffsets(mf, subValue);
    193           MatchFinder_Normalize3(subValue, mf->hash + mf->fixedHashSize, mf->hashMask + 1);
    194         }
    195         {
    196           UInt32 *heads = mt->hashBuf + ((numProcessedBlocks++) & kMtHashNumBlocksMask) * kMtHashBlockSize;
    197           UInt32 num = mf->streamPos - mf->pos;
    198           heads[0] = 2;
    199           heads[1] = num;
    200           if (num >= mf->numHashBytes)
    201           {
    202             num = num - mf->numHashBytes + 1;
    203             if (num > kMtHashBlockSize - 2)
    204               num = kMtHashBlockSize - 2;
    205             mt->GetHeadsFunc(mf->buffer, mf->pos, mf->hash + mf->fixedHashSize, mf->hashMask, heads + 2, num, mf->crc);
    206             heads[0] += num;
    207           }
    208           mf->pos += num;
    209           mf->buffer += num;
    210         }
    211       }
    212 
    213       Semaphore_Release1(&p->filledSemaphore);
    214     }
    215   }
    216 }
    217 
    218 void MatchFinderMt_GetNextBlock_Hash(CMatchFinderMt *p)
    219 {
    220   MtSync_GetNextBlock(&p->hashSync);
    221   p->hashBufPosLimit = p->hashBufPos = ((p->hashSync.numProcessedBlocks - 1) & kMtHashNumBlocksMask) * kMtHashBlockSize;
    222   p->hashBufPosLimit += p->hashBuf[p->hashBufPos++];
    223   p->hashNumAvail = p->hashBuf[p->hashBufPos++];
    224 }
    225 
    226 #define kEmptyHashValue 0
    227 
    228 /* #define MFMT_GM_INLINE */
    229 
    230 #ifdef MFMT_GM_INLINE
    231 
    232 #define NO_INLINE MY_FAST_CALL
    233 
    234 Int32 NO_INLINE GetMatchesSpecN(UInt32 lenLimit, UInt32 pos, const Byte *cur, CLzRef *son,
    235     UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue,
    236     UInt32 *_distances, UInt32 _maxLen, const UInt32 *hash, Int32 limit, UInt32 size, UInt32 *posRes)
    237 {
    238   do
    239   {
    240   UInt32 *distances = _distances + 1;
    241   UInt32 curMatch = pos - *hash++;
    242 
    243   CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1;
    244   CLzRef *ptr1 = son + (_cyclicBufferPos << 1);
    245   UInt32 len0 = 0, len1 = 0;
    246   UInt32 cutValue = _cutValue;
    247   UInt32 maxLen = _maxLen;
    248   for (;;)
    249   {
    250     UInt32 delta = pos - curMatch;
    251     if (cutValue-- == 0 || delta >= _cyclicBufferSize)
    252     {
    253       *ptr0 = *ptr1 = kEmptyHashValue;
    254       break;
    255     }
    256     {
    257       CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1);
    258       const Byte *pb = cur - delta;
    259       UInt32 len = (len0 < len1 ? len0 : len1);
    260       if (pb[len] == cur[len])
    261       {
    262         if (++len != lenLimit && pb[len] == cur[len])
    263           while (++len != lenLimit)
    264             if (pb[len] != cur[len])
    265               break;
    266         if (maxLen < len)
    267         {
    268           *distances++ = maxLen = len;
    269           *distances++ = delta - 1;
    270           if (len == lenLimit)
    271           {
    272             *ptr1 = pair[0];
    273             *ptr0 = pair[1];
    274             break;
    275           }
    276         }
    277       }
    278       if (pb[len] < cur[len])
    279       {
    280         *ptr1 = curMatch;
    281         ptr1 = pair + 1;
    282         curMatch = *ptr1;
    283         len1 = len;
    284       }
    285       else
    286       {
    287         *ptr0 = curMatch;
    288         ptr0 = pair;
    289         curMatch = *ptr0;
    290         len0 = len;
    291       }
    292     }
    293   }
    294   pos++;
    295   _cyclicBufferPos++;
    296   cur++;
    297   {
    298     UInt32 num = (UInt32)(distances - _distances);
    299     *_distances = num - 1;
    300     _distances += num;
    301     limit -= num;
    302   }
    303   }
    304   while (limit > 0 && --size != 0);
    305   *posRes = pos;
    306   return limit;
    307 }
    308 
    309 #endif
    310 
    311 void BtGetMatches(CMatchFinderMt *p, UInt32 *distances)
    312 {
    313   UInt32 numProcessed = 0;
    314   UInt32 curPos = 2;
    315   UInt32 limit = kMtBtBlockSize - (p->matchMaxLen * 2);
    316   distances[1] = p->hashNumAvail;
    317   while (curPos < limit)
    318   {
    319     if (p->hashBufPos == p->hashBufPosLimit)
    320     {
    321       MatchFinderMt_GetNextBlock_Hash(p);
    322       distances[1] = numProcessed + p->hashNumAvail;
    323       if (p->hashNumAvail >= p->numHashBytes)
    324         continue;
    325       for (; p->hashNumAvail != 0; p->hashNumAvail--)
    326         distances[curPos++] = 0;
    327       break;
    328     }
    329     {
    330       UInt32 size = p->hashBufPosLimit - p->hashBufPos;
    331       UInt32 lenLimit = p->matchMaxLen;
    332       UInt32 pos = p->pos;
    333       UInt32 cyclicBufferPos = p->cyclicBufferPos;
    334       if (lenLimit >= p->hashNumAvail)
    335         lenLimit = p->hashNumAvail;
    336       {
    337         UInt32 size2 = p->hashNumAvail - lenLimit + 1;
    338         if (size2 < size)
    339           size = size2;
    340         size2 = p->cyclicBufferSize - cyclicBufferPos;
    341         if (size2 < size)
    342           size = size2;
    343       }
    344       #ifndef MFMT_GM_INLINE
    345       while (curPos < limit && size-- != 0)
    346       {
    347         UInt32 *startDistances = distances + curPos;
    348         UInt32 num = (UInt32)(GetMatchesSpec1(lenLimit, pos - p->hashBuf[p->hashBufPos++],
    349           pos, p->buffer, p->son, cyclicBufferPos, p->cyclicBufferSize, p->cutValue,
    350           startDistances + 1, p->numHashBytes - 1) - startDistances);
    351         *startDistances = num - 1;
    352         curPos += num;
    353         cyclicBufferPos++;
    354         pos++;
    355         p->buffer++;
    356       }
    357       #else
    358       {
    359         UInt32 posRes;
    360         curPos = limit - GetMatchesSpecN(lenLimit, pos, p->buffer, p->son, cyclicBufferPos, p->cyclicBufferSize, p->cutValue,
    361           distances + curPos, p->numHashBytes - 1, p->hashBuf + p->hashBufPos, (Int32)(limit - curPos) , size, &posRes);
    362         p->hashBufPos += posRes - pos;
    363         cyclicBufferPos += posRes - pos;
    364         p->buffer += posRes - pos;
    365         pos = posRes;
    366       }
    367       #endif
    368 
    369       numProcessed += pos - p->pos;
    370       p->hashNumAvail -= pos - p->pos;
    371       p->pos = pos;
    372       if (cyclicBufferPos == p->cyclicBufferSize)
    373         cyclicBufferPos = 0;
    374       p->cyclicBufferPos = cyclicBufferPos;
    375     }
    376   }
    377   distances[0] = curPos;
    378 }
    379 
    380 void BtFillBlock(CMatchFinderMt *p, UInt32 globalBlockIndex)
    381 {
    382   CMtSync *sync = &p->hashSync;
    383   if (!sync->needStart)
    384   {
    385     CriticalSection_Enter(&sync->cs);
    386     sync->csWasEntered = True;
    387   }
    388 
    389   BtGetMatches(p, p->btBuf + (globalBlockIndex & kMtBtNumBlocksMask) * kMtBtBlockSize);
    390 
    391   if (p->pos > kMtMaxValForNormalize - kMtBtBlockSize)
    392   {
    393     UInt32 subValue = p->pos - p->cyclicBufferSize;
    394     MatchFinder_Normalize3(subValue, p->son, p->cyclicBufferSize * 2);
    395     p->pos -= subValue;
    396   }
    397 
    398   if (!sync->needStart)
    399   {
    400     CriticalSection_Leave(&sync->cs);
    401     sync->csWasEntered = False;
    402   }
    403 }
    404 
    405 void BtThreadFunc(CMatchFinderMt *mt)
    406 {
    407   CMtSync *p = &mt->btSync;
    408   for (;;)
    409   {
    410     UInt32 blockIndex = 0;
    411     Event_Wait(&p->canStart);
    412     Event_Set(&p->wasStarted);
    413     for (;;)
    414     {
    415       if (p->exit)
    416         return;
    417       if (p->stopWriting)
    418       {
    419         p->numProcessedBlocks = blockIndex;
    420         MtSync_StopWriting(&mt->hashSync);
    421         Event_Set(&p->wasStopped);
    422         break;
    423       }
    424       Semaphore_Wait(&p->freeSemaphore);
    425       BtFillBlock(mt, blockIndex++);
    426       Semaphore_Release1(&p->filledSemaphore);
    427     }
    428   }
    429 }
    430 
    431 void MatchFinderMt_Construct(CMatchFinderMt *p)
    432 {
    433   p->hashBuf = 0;
    434   MtSync_Construct(&p->hashSync);
    435   MtSync_Construct(&p->btSync);
    436 }
    437 
    438 void MatchFinderMt_FreeMem(CMatchFinderMt *p, ISzAlloc *alloc)
    439 {
    440   alloc->Free(alloc, p->hashBuf);
    441   p->hashBuf = 0;
    442 }
    443 
    444 void MatchFinderMt_Destruct(CMatchFinderMt *p, ISzAlloc *alloc)
    445 {
    446   MtSync_Destruct(&p->hashSync);
    447   MtSync_Destruct(&p->btSync);
    448   MatchFinderMt_FreeMem(p, alloc);
    449 }
    450 
    451 #define kHashBufferSize (kMtHashBlockSize * kMtHashNumBlocks)
    452 #define kBtBufferSize (kMtBtBlockSize * kMtBtNumBlocks)
    453 
    454 static unsigned MY_STD_CALL HashThreadFunc2(void *p) { HashThreadFunc((CMatchFinderMt *)p);  return 0; }
    455 static unsigned MY_STD_CALL BtThreadFunc2(void *p)
    456 {
    457   Byte allocaDummy[0x180];
    458   int i = 0;
    459   for (i = 0; i < 16; i++)
    460     allocaDummy[i] = (Byte)i;
    461   BtThreadFunc((CMatchFinderMt *)p);
    462   return 0;
    463 }
    464 
    465 SRes MatchFinderMt_Create(CMatchFinderMt *p, UInt32 historySize, UInt32 keepAddBufferBefore,
    466     UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ISzAlloc *alloc)
    467 {
    468   CMatchFinder *mf = p->MatchFinder;
    469   p->historySize = historySize;
    470   if (kMtBtBlockSize <= matchMaxLen * 4)
    471     return SZ_ERROR_PARAM;
    472   if (p->hashBuf == 0)
    473   {
    474     p->hashBuf = (UInt32 *)alloc->Alloc(alloc, (kHashBufferSize + kBtBufferSize) * sizeof(UInt32));
    475     if (p->hashBuf == 0)
    476       return SZ_ERROR_MEM;
    477     p->btBuf = p->hashBuf + kHashBufferSize;
    478   }
    479   keepAddBufferBefore += (kHashBufferSize + kBtBufferSize);
    480   keepAddBufferAfter += kMtHashBlockSize;
    481   if (!MatchFinder_Create(mf, historySize, keepAddBufferBefore, matchMaxLen, keepAddBufferAfter, alloc))
    482     return SZ_ERROR_MEM;
    483 
    484   RINOK(MtSync_Create(&p->hashSync, HashThreadFunc2, p, kMtHashNumBlocks));
    485   RINOK(MtSync_Create(&p->btSync, BtThreadFunc2, p, kMtBtNumBlocks));
    486   return SZ_OK;
    487 }
    488 
    489 /* Call it after ReleaseStream / SetStream */
    490 void MatchFinderMt_Init(CMatchFinderMt *p)
    491 {
    492   CMatchFinder *mf = p->MatchFinder;
    493   p->btBufPos = p->btBufPosLimit = 0;
    494   p->hashBufPos = p->hashBufPosLimit = 0;
    495   MatchFinder_Init(mf);
    496   p->pointerToCurPos = MatchFinder_GetPointerToCurrentPos(mf);
    497   p->btNumAvailBytes = 0;
    498   p->lzPos = p->historySize + 1;
    499 
    500   p->hash = mf->hash;
    501   p->fixedHashSize = mf->fixedHashSize;
    502   p->crc = mf->crc;
    503 
    504   p->son = mf->son;
    505   p->matchMaxLen = mf->matchMaxLen;
    506   p->numHashBytes = mf->numHashBytes;
    507   p->pos = mf->pos;
    508   p->buffer = mf->buffer;
    509   p->cyclicBufferPos = mf->cyclicBufferPos;
    510   p->cyclicBufferSize = mf->cyclicBufferSize;
    511   p->cutValue = mf->cutValue;
    512 }
    513 
    514 /* ReleaseStream is required to finish multithreading */
    515 void MatchFinderMt_ReleaseStream(CMatchFinderMt *p)
    516 {
    517   MtSync_StopWriting(&p->btSync);
    518   /* p->MatchFinder->ReleaseStream(); */
    519 }
    520 
    521 void MatchFinderMt_Normalize(CMatchFinderMt *p)
    522 {
    523   MatchFinder_Normalize3(p->lzPos - p->historySize - 1, p->hash, p->fixedHashSize);
    524   p->lzPos = p->historySize + 1;
    525 }
    526 
    527 void MatchFinderMt_GetNextBlock_Bt(CMatchFinderMt *p)
    528 {
    529   UInt32 blockIndex;
    530   MtSync_GetNextBlock(&p->btSync);
    531   blockIndex = ((p->btSync.numProcessedBlocks - 1) & kMtBtNumBlocksMask);
    532   p->btBufPosLimit = p->btBufPos = blockIndex * kMtBtBlockSize;
    533   p->btBufPosLimit += p->btBuf[p->btBufPos++];
    534   p->btNumAvailBytes = p->btBuf[p->btBufPos++];
    535   if (p->lzPos >= kMtMaxValForNormalize - kMtBtBlockSize)
    536     MatchFinderMt_Normalize(p);
    537 }
    538 
    539 const Byte * MatchFinderMt_GetPointerToCurrentPos(CMatchFinderMt *p)
    540 {
    541   return p->pointerToCurPos;
    542 }
    543 
    544 #define GET_NEXT_BLOCK_IF_REQUIRED if (p->btBufPos == p->btBufPosLimit) MatchFinderMt_GetNextBlock_Bt(p);
    545 
    546 UInt32 MatchFinderMt_GetNumAvailableBytes(CMatchFinderMt *p)
    547 {
    548   GET_NEXT_BLOCK_IF_REQUIRED;
    549   return p->btNumAvailBytes;
    550 }
    551 
    552 Byte MatchFinderMt_GetIndexByte(CMatchFinderMt *p, Int32 index)
    553 {
    554   return p->pointerToCurPos[index];
    555 }
    556 
    557 UInt32 * MixMatches2(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances)
    558 {
    559   UInt32 hash2Value, curMatch2;
    560   UInt32 *hash = p->hash;
    561   const Byte *cur = p->pointerToCurPos;
    562   UInt32 lzPos = p->lzPos;
    563   MT_HASH2_CALC
    564 
    565   curMatch2 = hash[hash2Value];
    566   hash[hash2Value] = lzPos;
    567 
    568   if (curMatch2 >= matchMinPos)
    569     if (cur[(ptrdiff_t)curMatch2 - lzPos] == cur[0])
    570     {
    571       *distances++ = 2;
    572       *distances++ = lzPos - curMatch2 - 1;
    573     }
    574   return distances;
    575 }
    576 
    577 UInt32 * MixMatches3(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances)
    578 {
    579   UInt32 hash2Value, hash3Value, curMatch2, curMatch3;
    580   UInt32 *hash = p->hash;
    581   const Byte *cur = p->pointerToCurPos;
    582   UInt32 lzPos = p->lzPos;
    583   MT_HASH3_CALC
    584 
    585   curMatch2 = hash[                hash2Value];
    586   curMatch3 = hash[kFix3HashSize + hash3Value];
    587 
    588   hash[                hash2Value] =
    589   hash[kFix3HashSize + hash3Value] =
    590     lzPos;
    591 
    592   if (curMatch2 >= matchMinPos && cur[(ptrdiff_t)curMatch2 - lzPos] == cur[0])
    593   {
    594     distances[1] = lzPos - curMatch2 - 1;
    595     if (cur[(ptrdiff_t)curMatch2 - lzPos + 2] == cur[2])
    596     {
    597       distances[0] = 3;
    598       return distances + 2;
    599     }
    600     distances[0] = 2;
    601     distances += 2;
    602   }
    603   if (curMatch3 >= matchMinPos && cur[(ptrdiff_t)curMatch3 - lzPos] == cur[0])
    604   {
    605     *distances++ = 3;
    606     *distances++ = lzPos - curMatch3 - 1;
    607   }
    608   return distances;
    609 }
    610 
    611 /*
    612 UInt32 *MixMatches4(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances)
    613 {
    614   UInt32 hash2Value, hash3Value, hash4Value, curMatch2, curMatch3, curMatch4;
    615   UInt32 *hash = p->hash;
    616   const Byte *cur = p->pointerToCurPos;
    617   UInt32 lzPos = p->lzPos;
    618   MT_HASH4_CALC
    619 
    620   curMatch2 = hash[                hash2Value];
    621   curMatch3 = hash[kFix3HashSize + hash3Value];
    622   curMatch4 = hash[kFix4HashSize + hash4Value];
    623 
    624   hash[                hash2Value] =
    625   hash[kFix3HashSize + hash3Value] =
    626   hash[kFix4HashSize + hash4Value] =
    627     lzPos;
    628 
    629   if (curMatch2 >= matchMinPos && cur[(ptrdiff_t)curMatch2 - lzPos] == cur[0])
    630   {
    631     distances[1] = lzPos - curMatch2 - 1;
    632     if (cur[(ptrdiff_t)curMatch2 - lzPos + 2] == cur[2])
    633     {
    634       distances[0] =  (cur[(ptrdiff_t)curMatch2 - lzPos + 3] == cur[3]) ? 4 : 3;
    635       return distances + 2;
    636     }
    637     distances[0] = 2;
    638     distances += 2;
    639   }
    640   if (curMatch3 >= matchMinPos && cur[(ptrdiff_t)curMatch3 - lzPos] == cur[0])
    641   {
    642     distances[1] = lzPos - curMatch3 - 1;
    643     if (cur[(ptrdiff_t)curMatch3 - lzPos + 3] == cur[3])
    644     {
    645       distances[0] = 4;
    646       return distances + 2;
    647     }
    648     distances[0] = 3;
    649     distances += 2;
    650   }
    651 
    652   if (curMatch4 >= matchMinPos)
    653     if (
    654       cur[(ptrdiff_t)curMatch4 - lzPos] == cur[0] &&
    655       cur[(ptrdiff_t)curMatch4 - lzPos + 3] == cur[3]
    656       )
    657     {
    658       *distances++ = 4;
    659       *distances++ = lzPos - curMatch4 - 1;
    660     }
    661   return distances;
    662 }
    663 */
    664 
    665 #define INCREASE_LZ_POS p->lzPos++; p->pointerToCurPos++;
    666 
    667 UInt32 MatchFinderMt2_GetMatches(CMatchFinderMt *p, UInt32 *distances)
    668 {
    669   const UInt32 *btBuf = p->btBuf + p->btBufPos;
    670   UInt32 len = *btBuf++;
    671   p->btBufPos += 1 + len;
    672   p->btNumAvailBytes--;
    673   {
    674     UInt32 i;
    675     for (i = 0; i < len; i += 2)
    676     {
    677       *distances++ = *btBuf++;
    678       *distances++ = *btBuf++;
    679     }
    680   }
    681   INCREASE_LZ_POS
    682   return len;
    683 }
    684 
    685 UInt32 MatchFinderMt_GetMatches(CMatchFinderMt *p, UInt32 *distances)
    686 {
    687   const UInt32 *btBuf = p->btBuf + p->btBufPos;
    688   UInt32 len = *btBuf++;
    689   p->btBufPos += 1 + len;
    690 
    691   if (len == 0)
    692   {
    693     if (p->btNumAvailBytes-- >= 4)
    694       len = (UInt32)(p->MixMatchesFunc(p, p->lzPos - p->historySize, distances) - (distances));
    695   }
    696   else
    697   {
    698     /* Condition: there are matches in btBuf with length < p->numHashBytes */
    699     UInt32 *distances2;
    700     p->btNumAvailBytes--;
    701     distances2 = p->MixMatchesFunc(p, p->lzPos - btBuf[1], distances);
    702     do
    703     {
    704       *distances2++ = *btBuf++;
    705       *distances2++ = *btBuf++;
    706     }
    707     while ((len -= 2) != 0);
    708     len  = (UInt32)(distances2 - (distances));
    709   }
    710   INCREASE_LZ_POS
    711   return len;
    712 }
    713 
    714 #define SKIP_HEADER2_MT  do { GET_NEXT_BLOCK_IF_REQUIRED
    715 #define SKIP_HEADER_MT(n) SKIP_HEADER2_MT if (p->btNumAvailBytes-- >= (n)) { const Byte *cur = p->pointerToCurPos; UInt32 *hash = p->hash;
    716 #define SKIP_FOOTER_MT } INCREASE_LZ_POS p->btBufPos += p->btBuf[p->btBufPos] + 1; } while (--num != 0);
    717 
    718 void MatchFinderMt0_Skip(CMatchFinderMt *p, UInt32 num)
    719 {
    720   SKIP_HEADER2_MT { p->btNumAvailBytes--;
    721   SKIP_FOOTER_MT
    722 }
    723 
    724 void MatchFinderMt2_Skip(CMatchFinderMt *p, UInt32 num)
    725 {
    726   SKIP_HEADER_MT(2)
    727       UInt32 hash2Value;
    728       MT_HASH2_CALC
    729       hash[hash2Value] = p->lzPos;
    730   SKIP_FOOTER_MT
    731 }
    732 
    733 void MatchFinderMt3_Skip(CMatchFinderMt *p, UInt32 num)
    734 {
    735   SKIP_HEADER_MT(3)
    736       UInt32 hash2Value, hash3Value;
    737       MT_HASH3_CALC
    738       hash[kFix3HashSize + hash3Value] =
    739       hash[                hash2Value] =
    740         p->lzPos;
    741   SKIP_FOOTER_MT
    742 }
    743 
    744 /*
    745 void MatchFinderMt4_Skip(CMatchFinderMt *p, UInt32 num)
    746 {
    747   SKIP_HEADER_MT(4)
    748       UInt32 hash2Value, hash3Value, hash4Value;
    749       MT_HASH4_CALC
    750       hash[kFix4HashSize + hash4Value] =
    751       hash[kFix3HashSize + hash3Value] =
    752       hash[                hash2Value] =
    753         p->lzPos;
    754   SKIP_FOOTER_MT
    755 }
    756 */
    757 
    758 void MatchFinderMt_CreateVTable(CMatchFinderMt *p, IMatchFinder *vTable)
    759 {
    760   vTable->Init = (Mf_Init_Func)MatchFinderMt_Init;
    761   vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinderMt_GetIndexByte;
    762   vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinderMt_GetNumAvailableBytes;
    763   vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinderMt_GetPointerToCurrentPos;
    764   vTable->GetMatches = (Mf_GetMatches_Func)MatchFinderMt_GetMatches;
    765   switch(p->MatchFinder->numHashBytes)
    766   {
    767     case 2:
    768       p->GetHeadsFunc = GetHeads2;
    769       p->MixMatchesFunc = (Mf_Mix_Matches)0;
    770       vTable->Skip = (Mf_Skip_Func)MatchFinderMt0_Skip;
    771       vTable->GetMatches = (Mf_GetMatches_Func)MatchFinderMt2_GetMatches;
    772       break;
    773     case 3:
    774       p->GetHeadsFunc = GetHeads3;
    775       p->MixMatchesFunc = (Mf_Mix_Matches)MixMatches2;
    776       vTable->Skip = (Mf_Skip_Func)MatchFinderMt2_Skip;
    777       break;
    778     default:
    779     /* case 4: */
    780       p->GetHeadsFunc = p->MatchFinder->bigHash ? GetHeads4b : GetHeads4;
    781       /* p->GetHeadsFunc = GetHeads4; */
    782       p->MixMatchesFunc = (Mf_Mix_Matches)MixMatches3;
    783       vTable->Skip = (Mf_Skip_Func)MatchFinderMt3_Skip;
    784       break;
    785     /*
    786     default:
    787       p->GetHeadsFunc = GetHeads5;
    788       p->MixMatchesFunc = (Mf_Mix_Matches)MixMatches4;
    789       vTable->Skip = (Mf_Skip_Func)MatchFinderMt4_Skip;
    790       break;
    791     */
    792   }
    793 }
    794