📄 deflateencoder.cpp
字号:
// DeflateEncoder.cpp
#include "StdAfx.h"
#include <stdio.h>
#include "DeflateEncoder.h"
#include "Windows/Defs.h"
#include "Common/ComTry.h"
extern "C"
{
#include "../../../../C/Alloc.h"
#include "../../../../C/HuffEnc.h"
}
#if _MSC_VER >= 1300
#define NO_INLINE __declspec(noinline)
#else
#define NO_INLINE
#endif
namespace NCompress {
namespace NDeflate {
namespace NEncoder {
const int kNumDivPassesMax = 10; // [0, 16); ratio/speed/ram tradeoff; use big value for better compression ratio.
const UInt32 kNumTables = (1 << kNumDivPassesMax);
static UInt32 kFixedHuffmanCodeBlockSizeMax = (1 << 8); // [0, (1 << 32)); ratio/speed tradeoff; use big value for better compression ratio.
static UInt32 kDivideCodeBlockSizeMin = (1 << 7); // [1, (1 << 32)); ratio/speed tradeoff; use small value for better compression ratio.
static UInt32 kDivideBlockSizeMin = (1 << 6); // [1, (1 << 32)); ratio/speed tradeoff; use small value for better compression ratio.
static const UInt32 kMaxUncompressedBlockSize = ((1 << 16) - 1) * 1; // [1, (1 << 32))
static const UInt32 kMatchArraySize = kMaxUncompressedBlockSize * 10; // [kMatchMaxLen * 2, (1 << 32))
static const UInt32 kMatchArrayLimit = kMatchArraySize - kMatchMaxLen * 4 * sizeof(UInt16);
static const UInt32 kBlockUncompressedSizeThreshold = kMaxUncompressedBlockSize -
kMatchMaxLen - kNumOpts;
static const int kMaxCodeBitLength = 11;
static const int kMaxLevelBitLength = 7;
static Byte kNoLiteralStatPrice = 11;
static Byte kNoLenStatPrice = 11;
static Byte kNoPosStatPrice = 6;
static Byte g_LenSlots[kNumLenSymbolsMax];
static Byte g_FastPos[1 << 9];
class CFastPosInit
{
public:
CFastPosInit()
{
int i;
for(i = 0; i < kNumLenSlots; i++)
{
int c = kLenStart32[i];
int j = 1 << kLenDirectBits32[i];
for(int k = 0; k < j; k++, c++)
g_LenSlots[c] = (Byte)i;
}
const int kFastSlots = 18;
int c = 0;
for (Byte slotFast = 0; slotFast < kFastSlots; slotFast++)
{
UInt32 k = (1 << kDistDirectBits[slotFast]);
for (UInt32 j = 0; j < k; j++, c++)
g_FastPos[c] = slotFast;
}
}
};
static CFastPosInit g_FastPosInit;
inline UInt32 GetPosSlot(UInt32 pos)
{
if (pos < 0x200)
return g_FastPos[pos];
return g_FastPos[pos >> 8] + 16;
}
static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); }
static void SzFree(void *p, void *address) { p = p; MyFree(address); }
static ISzAlloc g_Alloc = { SzAlloc, SzFree };
CCoder::CCoder(bool deflate64Mode):
m_Deflate64Mode(deflate64Mode),
m_NumPasses(1),
m_NumDivPasses(1),
m_NumFastBytes(32),
_fastMode(false),
_btMode(true),
m_OnePosMatchesMemory(0),
m_DistanceMemory(0),
m_Created(false),
m_Values(0),
m_Tables(0),
m_MatchFinderCycles(0)
// m_SetMfPasses(0)
{
m_MatchMaxLen = deflate64Mode ? kMatchMaxLen64 : kMatchMaxLen32;
m_NumLenCombinations = deflate64Mode ? kNumLenSymbols64 : kNumLenSymbols32;
m_LenStart = deflate64Mode ? kLenStart64 : kLenStart32;
m_LenDirectBits = deflate64Mode ? kLenDirectBits64 : kLenDirectBits32;
MatchFinder_Construct(&_lzInWindow);
}
HRESULT CCoder::Create()
{
COM_TRY_BEGIN
if (m_Values == 0)
{
m_Values = (CCodeValue *)MyAlloc((kMaxUncompressedBlockSize) * sizeof(CCodeValue));
if (m_Values == 0)
return E_OUTOFMEMORY;
}
if (m_Tables == 0)
{
m_Tables = (CTables *)MyAlloc((kNumTables) * sizeof(CTables));
if (m_Tables == 0)
return E_OUTOFMEMORY;
}
if (m_IsMultiPass)
{
if (m_OnePosMatchesMemory == 0)
{
m_OnePosMatchesMemory = (UInt16 *)::MidAlloc(kMatchArraySize * sizeof(UInt16));
if (m_OnePosMatchesMemory == 0)
return E_OUTOFMEMORY;
}
}
else
{
if (m_DistanceMemory == 0)
{
m_DistanceMemory = (UInt16 *)MyAlloc((kMatchMaxLen + 2) * 2 * sizeof(UInt16));
if (m_DistanceMemory == 0)
return E_OUTOFMEMORY;
m_MatchDistances = m_DistanceMemory;
}
}
if (!m_Created)
{
_lzInWindow.btMode = _btMode ? 1 : 0;
_lzInWindow.numHashBytes = 3;
if (!MatchFinder_Create(&_lzInWindow,
m_Deflate64Mode ? kHistorySize64 : kHistorySize32,
kNumOpts + kMaxUncompressedBlockSize,
m_NumFastBytes, m_MatchMaxLen - m_NumFastBytes, &g_Alloc))
return E_OUTOFMEMORY;
if (!m_OutStream.Create(1 << 20))
return E_OUTOFMEMORY;
}
if (m_MatchFinderCycles != 0)
_lzInWindow.cutValue = m_MatchFinderCycles;
m_Created = true;
return S_OK;
COM_TRY_END
}
// ICompressSetEncoderProperties2
HRESULT CCoder::BaseSetEncoderProperties2(const PROPID *propIDs,
const PROPVARIANT *properties, UInt32 numProperties)
{
for(UInt32 i = 0; i < numProperties; i++)
{
const PROPVARIANT &prop = properties[i];
switch(propIDs[i])
{
case NCoderPropID::kNumPasses:
if (prop.vt != VT_UI4)
return E_INVALIDARG;
m_NumDivPasses = prop.ulVal;
if (m_NumDivPasses == 0)
m_NumDivPasses = 1;
if (m_NumDivPasses == 1)
m_NumPasses = 1;
else if (m_NumDivPasses <= kNumDivPassesMax)
m_NumPasses = 2;
else
{
m_NumPasses = 2 + (m_NumDivPasses - kNumDivPassesMax);
m_NumDivPasses = kNumDivPassesMax;
}
break;
case NCoderPropID::kNumFastBytes:
if (prop.vt != VT_UI4)
return E_INVALIDARG;
m_NumFastBytes = prop.ulVal;
if(m_NumFastBytes < kMatchMinLen || m_NumFastBytes > m_MatchMaxLen)
return E_INVALIDARG;
break;
case NCoderPropID::kMatchFinderCycles:
{
if (prop.vt != VT_UI4)
return E_INVALIDARG;
m_MatchFinderCycles = prop.ulVal;
break;
}
case NCoderPropID::kAlgorithm:
{
if (prop.vt != VT_UI4)
return E_INVALIDARG;
UInt32 maximize = prop.ulVal;
_fastMode = (maximize == 0);
_btMode = !_fastMode;
break;
}
default:
return E_INVALIDARG;
}
}
return S_OK;
}
void CCoder::Free()
{
::MidFree(m_OnePosMatchesMemory);
m_OnePosMatchesMemory = 0;
::MyFree(m_DistanceMemory);
m_DistanceMemory = 0;
::MyFree(m_Values);
m_Values = 0;
::MyFree(m_Tables);
m_Tables = 0;
}
CCoder::~CCoder()
{
Free();
MatchFinder_Free(&_lzInWindow, &g_Alloc);
}
NO_INLINE void CCoder::GetMatches()
{
if (m_IsMultiPass)
{
m_MatchDistances = m_OnePosMatchesMemory + m_Pos;
if (m_SecondPass)
{
m_Pos += *m_MatchDistances + 1;
return;
}
}
UInt32 distanceTmp[kMatchMaxLen * 2 + 3];
UInt32 numPairs = (_btMode) ?
Bt3Zip_MatchFinder_GetMatches(&_lzInWindow, distanceTmp):
Hc3Zip_MatchFinder_GetMatches(&_lzInWindow, distanceTmp);
*m_MatchDistances = (UInt16)numPairs;
if (numPairs > 0)
{
UInt32 i;
for(i = 0; i < numPairs; i += 2)
{
m_MatchDistances[i + 1] = (UInt16)distanceTmp[i];
m_MatchDistances[i + 2] = (UInt16)distanceTmp[i + 1];
}
UInt32 len = distanceTmp[numPairs - 2];
if (len == m_NumFastBytes && m_NumFastBytes != m_MatchMaxLen)
{
UInt32 numAvail = Inline_MatchFinder_GetNumAvailableBytes(&_lzInWindow) + 1;
const Byte *pby = Inline_MatchFinder_GetPointerToCurrentPos(&_lzInWindow) - 1;
const Byte *pby2 = pby - (distanceTmp[numPairs - 1] + 1);
if (numAvail > m_MatchMaxLen)
numAvail = m_MatchMaxLen;
for (; len < numAvail && pby[len] == pby2[len]; len++);
m_MatchDistances[i - 1] = (UInt16)len;
}
}
if (m_IsMultiPass)
m_Pos += numPairs + 1;
if (!m_SecondPass)
m_AdditionalOffset++;
}
void CCoder::MovePos(UInt32 num)
{
if (!m_SecondPass && num > 0)
{
if (_btMode)
Bt3Zip_MatchFinder_Skip(&_lzInWindow, num);
else
Hc3Zip_MatchFinder_Skip(&_lzInWindow, num);
m_AdditionalOffset += num;
}
}
static const UInt32 kIfinityPrice = 0xFFFFFFF;
NO_INLINE UInt32 CCoder::Backward(UInt32 &backRes, UInt32 cur)
{
m_OptimumEndIndex = cur;
UInt32 posMem = m_Optimum[cur].PosPrev;
UInt16 backMem = m_Optimum[cur].BackPrev;
do
{
UInt32 posPrev = posMem;
UInt16 backCur = backMem;
backMem = m_Optimum[posPrev].BackPrev;
posMem = m_Optimum[posPrev].PosPrev;
m_Optimum[posPrev].BackPrev = backCur;
m_Optimum[posPrev].PosPrev = (UInt16)cur;
cur = posPrev;
}
while(cur > 0);
backRes = m_Optimum[0].BackPrev;
m_OptimumCurrentIndex = m_Optimum[0].PosPrev;
return m_OptimumCurrentIndex;
}
NO_INLINE UInt32 CCoder::GetOptimal(UInt32 &backRes)
{
if(m_OptimumEndIndex != m_OptimumCurrentIndex)
{
UInt32 len = m_Optimum[m_OptimumCurrentIndex].PosPrev - m_OptimumCurrentIndex;
backRes = m_Optimum[m_OptimumCurrentIndex].BackPrev;
m_OptimumCurrentIndex = m_Optimum[m_OptimumCurrentIndex].PosPrev;
return len;
}
m_OptimumCurrentIndex = m_OptimumEndIndex = 0;
GetMatches();
UInt32 numDistancePairs = m_MatchDistances[0];
if(numDistancePairs == 0)
return 1;
const UInt16 *matchDistances = m_MatchDistances + 1;
UInt32 lenMain = matchDistances[numDistancePairs - 2];
if(lenMain > m_NumFastBytes)
{
backRes = matchDistances[numDistancePairs - 1];
MovePos(lenMain - 1);
return lenMain;
}
m_Optimum[1].Price = m_LiteralPrices[Inline_MatchFinder_GetIndexByte(&_lzInWindow, 0 - m_AdditionalOffset)];
m_Optimum[1].PosPrev = 0;
m_Optimum[2].Price = kIfinityPrice;
m_Optimum[2].PosPrev = 1;
UInt32 offs = 0;
for(UInt32 i = kMatchMinLen; i <= lenMain; i++)
{
UInt32 distance = matchDistances[offs + 1];
m_Optimum[i].PosPrev = 0;
m_Optimum[i].BackPrev = (UInt16)distance;
m_Optimum[i].Price = m_LenPrices[i - kMatchMinLen] + m_PosPrices[GetPosSlot(distance)];
if (i == matchDistances[offs])
offs += 2;
}
UInt32 cur = 0;
UInt32 lenEnd = lenMain;
for (;;)
{
++cur;
if(cur == lenEnd || cur == kNumOptsBase || m_Pos >= kMatchArrayLimit)
return Backward(backRes, cur);
GetMatches();
matchDistances = m_MatchDistances + 1;
UInt32 numDistancePairs = m_MatchDistances[0];
UInt32 newLen = 0;
if(numDistancePairs != 0)
{
newLen = matchDistances[numDistancePairs - 2];
if(newLen > m_NumFastBytes)
{
UInt32 len = Backward(backRes, cur);
m_Optimum[cur].BackPrev = matchDistances[numDistancePairs - 1];
m_OptimumEndIndex = cur + newLen;
m_Optimum[cur].PosPrev = (UInt16)m_OptimumEndIndex;
MovePos(newLen - 1);
return len;
}
}
UInt32 curPrice = m_Optimum[cur].Price;
UInt32 curAnd1Price = curPrice + m_LiteralPrices[Inline_MatchFinder_GetIndexByte(&_lzInWindow, cur - m_AdditionalOffset)];
COptimal &optimum = m_Optimum[cur + 1];
if (curAnd1Price < optimum.Price)
{
optimum.Price = curAnd1Price;
optimum.PosPrev = (UInt16)cur;
}
if(numDistancePairs == 0)
continue;
while(lenEnd < cur + newLen)
m_Optimum[++lenEnd].Price = kIfinityPrice;
offs = 0;
UInt32 distance = matchDistances[offs + 1];
curPrice += m_PosPrices[GetPosSlot(distance)];
for(UInt32 lenTest = kMatchMinLen; ; lenTest++)
{
UInt32 curAndLenPrice = curPrice + m_LenPrices[lenTest - kMatchMinLen];
COptimal &optimum = m_Optimum[cur + lenTest];
if (curAndLenPrice < optimum.Price)
{
optimum.Price = curAndLenPrice;
optimum.PosPrev = (UInt16)cur;
optimum.BackPrev = (UInt16)distance;
}
if (lenTest == matchDistances[offs])
{
offs += 2;
if (offs == numDistancePairs)
break;
curPrice -= m_PosPrices[GetPosSlot(distance)];
distance = matchDistances[offs + 1];
curPrice += m_PosPrices[GetPosSlot(distance)];
}
}
}
}
UInt32 CCoder::GetOptimalFast(UInt32 &backRes)
{
GetMatches();
UInt32 numDistancePairs = m_MatchDistances[0];
if (numDistancePairs == 0)
return 1;
UInt32 lenMain = m_MatchDistances[numDistancePairs - 1];
backRes = m_MatchDistances[numDistancePairs];
MovePos(lenMain - 1);
return lenMain;
}
void CTables::InitStructures()
{
UInt32 i;
for(i = 0; i < 256; i++)
litLenLevels[i] = 8;
litLenLevels[i++] = 13;
for(;i < kFixedMainTableSize; i++)
litLenLevels[i] = 5;
for(i = 0; i < kFixedDistTableSize; i++)
distLevels[i] = 5;
}
NO_INLINE void CCoder::LevelTableDummy(const Byte *levels, int numLevels, UInt32 *freqs)
{
int prevLen = 0xFF;
int nextLen = levels[0];
int count = 0;
int maxCount = 7;
int minCount = 4;
if (nextLen == 0)
{
maxCount = 138;
minCount = 3;
}
for (int n = 0; n < numLevels; n++)
{
int curLen = nextLen;
nextLen = (n < numLevels - 1) ? levels[n + 1] : 0xFF;
count++;
if (count < maxCount && curLen == nextLen)
continue;
if (count < minCount)
freqs[curLen] += (UInt32)count;
else if (curLen != 0)
{
if (curLen != prevLen)
{
freqs[curLen]++;
count--;
}
freqs[kTableLevelRepNumber]++;
}
else if (count <= 10)
freqs[kTableLevel0Number]++;
else
freqs[kTableLevel0Number2]++;
count = 0;
prevLen = curLen;
if (nextLen == 0)
{
maxCount = 138;
minCount = 3;
}
else if (curLen == nextLen)
{
maxCount = 6;
minCount = 3;
}
else
{
maxCount = 7;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -