mirror of
https://github.com/Xevion/easy7zip.git
synced 2025-12-11 00:07:09 -06:00
802 lines
23 KiB
C++
Executable File
802 lines
23 KiB
C++
Executable File
// DeflateEncoder.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#include "DeflateEncoder.h"
|
|
#include "DeflateConst.h"
|
|
|
|
#include "Windows/Defs.h"
|
|
#include "Common/ComTry.h"
|
|
#include "../../../Common/Alloc.h"
|
|
#include "../LZ/BinTree/BinTree3Z.h"
|
|
|
|
namespace NCompress {
|
|
namespace NDeflate {
|
|
namespace NEncoder {
|
|
|
|
class CMatchFinderException
|
|
{
|
|
public:
|
|
HRESULT m_Result;
|
|
CMatchFinderException(HRESULT result): m_Result (result) {}
|
|
};
|
|
|
|
static const int kValueBlockSize = 0x2000;
|
|
|
|
static const int kMaxCodeBitLength = 15;
|
|
static const int kMaxLevelBitLength = 7;
|
|
|
|
static const Byte kFlagImm = 0;
|
|
static const Byte kFlagLenPos = 4;
|
|
|
|
static const UInt32 kMaxUncompressedBlockSize = 0xFFFF; // test it !!!
|
|
|
|
static const UInt32 kBlockUncompressedSizeThreshold =
|
|
kMaxUncompressedBlockSize - kMatchMaxLen32 - kNumOpts;
|
|
|
|
static const int kNumGoodBacks = 0x10000;
|
|
|
|
static Byte kNoLiteralDummy = 13;
|
|
static Byte kNoLenDummy = 13;
|
|
static Byte kNoPosDummy = 6;
|
|
|
|
static Byte g_LenSlots[kNumLenCombinations32];
|
|
static Byte g_FastPos[1 << 9];
|
|
|
|
class CFastPosInit
|
|
{
|
|
public:
|
|
CFastPosInit()
|
|
{
|
|
int i;
|
|
for(i = 0; i < kLenTableSize; 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)
|
|
{
|
|
// for (UInt32 i = 1; pos >= kDistStart[i]; i++);
|
|
// return i - 1;
|
|
if (pos < 0x200)
|
|
return g_FastPos[pos];
|
|
return g_FastPos[pos >> 8] + 16;
|
|
}
|
|
|
|
CCoder::CCoder(bool deflate64Mode):
|
|
_deflate64Mode(deflate64Mode),
|
|
m_NumPasses(1),
|
|
m_NumFastBytes(32),
|
|
m_OnePosMatchesMemory(0),
|
|
m_OnePosMatchesArray(0),
|
|
m_MatchDistances(0),
|
|
m_Created(false),
|
|
m_Values(0)
|
|
{
|
|
m_MatchMaxLen = deflate64Mode ? kMatchMaxLen64 : kMatchMaxLen32;
|
|
m_NumLenCombinations = deflate64Mode ? kNumLenCombinations64 :
|
|
kNumLenCombinations32;
|
|
m_LenStart = deflate64Mode ? kLenStart64 : kLenStart32;
|
|
m_LenDirectBits = deflate64Mode ? kLenDirectBits64 : kLenDirectBits32;
|
|
}
|
|
|
|
HRESULT CCoder::Create()
|
|
{
|
|
COM_TRY_BEGIN
|
|
if (!m_MatchFinder)
|
|
{
|
|
m_MatchFinder = new NBT3Z::CMatchFinderBinTree;
|
|
if (m_MatchFinder == 0)
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
if (m_Values == 0)
|
|
{
|
|
m_Values = (CCodeValue *)MyAlloc((kValueBlockSize + kNumOpts) * sizeof(CCodeValue));
|
|
if (m_Values == 0)
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
RINOK(m_MatchFinder->Create(_deflate64Mode ? kHistorySize64 : kHistorySize32,
|
|
kNumOpts + kNumGoodBacks, m_NumFastBytes, m_MatchMaxLen - m_NumFastBytes));
|
|
if (!m_OutStream.Create(1 << 20))
|
|
return E_OUTOFMEMORY;
|
|
m_MatchLengthEdge = m_NumFastBytes + 1;
|
|
|
|
Free();
|
|
if (m_NumPasses > 1)
|
|
{
|
|
m_OnePosMatchesMemory = (UInt16 *)BigAlloc(kNumGoodBacks * (m_NumFastBytes + 1) * sizeof(UInt16));
|
|
if (m_OnePosMatchesMemory == 0)
|
|
return E_OUTOFMEMORY;
|
|
m_OnePosMatchesArray = (COnePosMatches *)MyAlloc(kNumGoodBacks * sizeof(COnePosMatches));
|
|
if (m_OnePosMatchesArray == 0)
|
|
return E_OUTOFMEMORY;
|
|
UInt16 *goodBacksWordsCurrent = m_OnePosMatchesMemory;
|
|
for(int i = 0; i < kNumGoodBacks; i++, goodBacksWordsCurrent += (m_NumFastBytes + 1))
|
|
m_OnePosMatchesArray[i].Init(goodBacksWordsCurrent);
|
|
}
|
|
else
|
|
{
|
|
m_MatchDistances = (UInt16 *)MyAlloc((m_NumFastBytes + 1) * sizeof(UInt16));
|
|
if (m_MatchDistances == 0)
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
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 &property = properties[i];
|
|
switch(propIDs[i])
|
|
{
|
|
case NCoderPropID::kNumPasses:
|
|
if (property.vt != VT_UI4)
|
|
return E_INVALIDARG;
|
|
m_NumPasses = property.ulVal;
|
|
if(m_NumPasses == 0 || m_NumPasses > 255)
|
|
return E_INVALIDARG;
|
|
break;
|
|
case NCoderPropID::kNumFastBytes:
|
|
if (property.vt != VT_UI4)
|
|
return E_INVALIDARG;
|
|
m_NumFastBytes = property.ulVal;
|
|
if(m_NumFastBytes < 3 || m_NumFastBytes > m_MatchMaxLen)
|
|
return E_INVALIDARG;
|
|
break;
|
|
default:
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
void CCoder::Free()
|
|
{
|
|
if(m_NumPasses > 0)
|
|
{
|
|
if (m_NumPasses > 1)
|
|
{
|
|
BigFree(m_OnePosMatchesMemory);
|
|
MyFree(m_OnePosMatchesArray);
|
|
}
|
|
else
|
|
MyFree(m_MatchDistances);
|
|
}
|
|
}
|
|
|
|
CCoder::~CCoder()
|
|
{
|
|
Free();
|
|
MyFree(m_Values);
|
|
}
|
|
|
|
void CCoder::ReadGoodBacks()
|
|
{
|
|
UInt32 goodIndex;
|
|
if (m_NumPasses > 1)
|
|
{
|
|
goodIndex = m_FinderPos % kNumGoodBacks;
|
|
m_MatchDistances = m_OnePosMatchesArray[goodIndex].MatchDistances;
|
|
}
|
|
UInt32 distanceTmp[kMatchMaxLen32 + 1];
|
|
UInt32 len = m_MatchFinder->GetLongestMatch(distanceTmp);
|
|
for(UInt32 i = kMatchMinLen; i <= len; i++)
|
|
m_MatchDistances[i] = (UInt16)distanceTmp[i];
|
|
|
|
m_LongestMatchDistance = m_MatchDistances[len];
|
|
if (len == m_NumFastBytes && m_NumFastBytes != m_MatchMaxLen)
|
|
m_LongestMatchLength = len + m_MatchFinder->GetMatchLen(len,
|
|
m_LongestMatchDistance, m_MatchMaxLen - len);
|
|
else
|
|
m_LongestMatchLength = len;
|
|
if (m_NumPasses > 1)
|
|
{
|
|
m_OnePosMatchesArray[goodIndex].LongestMatchDistance = UInt16(m_LongestMatchDistance);
|
|
m_OnePosMatchesArray[goodIndex].LongestMatchLength = UInt16(m_LongestMatchLength);
|
|
}
|
|
HRESULT result = m_MatchFinder->MovePos();
|
|
if (result != S_OK)
|
|
throw CMatchFinderException(result);
|
|
m_FinderPos++;
|
|
m_AdditionalOffset++;
|
|
}
|
|
|
|
void CCoder::GetBacks(UInt32 pos)
|
|
{
|
|
if(pos == m_FinderPos)
|
|
ReadGoodBacks();
|
|
else
|
|
{
|
|
if (m_NumPasses == 1)
|
|
{
|
|
if(pos + 1 == m_FinderPos)
|
|
return;
|
|
throw 1932;
|
|
}
|
|
else
|
|
{
|
|
UInt32 goodIndex = pos % kNumGoodBacks;
|
|
m_MatchDistances = m_OnePosMatchesArray[goodIndex].MatchDistances;
|
|
m_LongestMatchDistance = m_OnePosMatchesArray[goodIndex].LongestMatchDistance;
|
|
m_LongestMatchLength = m_OnePosMatchesArray[goodIndex].LongestMatchLength;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CCoder::MovePos(UInt32 num)
|
|
{
|
|
if (m_NumPasses > 1)
|
|
{
|
|
for(UInt32 i = 0; i < num; i++)
|
|
GetBacks(UInt32(m_BlockStartPostion + m_CurrentBlockUncompressedSize + i + 1));
|
|
}
|
|
else
|
|
{
|
|
for (;num > 0; num--)
|
|
{
|
|
m_MatchFinder->DummyLongestMatch();
|
|
HRESULT result = m_MatchFinder->MovePos();
|
|
if (result != S_OK)
|
|
throw CMatchFinderException(result);
|
|
m_FinderPos++;
|
|
m_AdditionalOffset++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static const UInt32 kIfinityPrice = 0xFFFFFFF;
|
|
|
|
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;
|
|
}
|
|
|
|
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 = 0;
|
|
m_OptimumEndIndex = 0;
|
|
|
|
GetBacks(UInt32(m_BlockStartPostion + m_CurrentBlockUncompressedSize));
|
|
|
|
UInt32 lenMain = m_LongestMatchLength;
|
|
UInt32 backMain = m_LongestMatchDistance;
|
|
|
|
if(lenMain < kMatchMinLen)
|
|
return 1;
|
|
if(lenMain >= m_MatchLengthEdge)
|
|
{
|
|
backRes = backMain;
|
|
MovePos(lenMain - 1);
|
|
return lenMain;
|
|
}
|
|
m_Optimum[1].Price = m_LiteralPrices[m_MatchFinder->GetIndexByte(0 - m_AdditionalOffset)];
|
|
m_Optimum[1].PosPrev = 0;
|
|
|
|
m_Optimum[2].Price = kIfinityPrice;
|
|
m_Optimum[2].PosPrev = 1;
|
|
|
|
for(UInt32 i = kMatchMinLen; i <= lenMain; i++)
|
|
{
|
|
m_Optimum[i].PosPrev = 0;
|
|
m_Optimum[i].BackPrev = m_MatchDistances[i];
|
|
m_Optimum[i].Price = m_LenPrices[i - kMatchMinLen] + m_PosPrices[GetPosSlot(m_MatchDistances[i])];
|
|
}
|
|
|
|
|
|
UInt32 cur = 0;
|
|
UInt32 lenEnd = lenMain;
|
|
while(true)
|
|
{
|
|
cur++;
|
|
if(cur == lenEnd)
|
|
return Backward(backRes, cur);
|
|
GetBacks(UInt32(m_BlockStartPostion + m_CurrentBlockUncompressedSize + cur));
|
|
UInt32 newLen = m_LongestMatchLength;
|
|
if(newLen >= m_MatchLengthEdge)
|
|
return Backward(backRes, cur);
|
|
|
|
UInt32 curPrice = m_Optimum[cur].Price;
|
|
UInt32 curAnd1Price = curPrice +
|
|
m_LiteralPrices[m_MatchFinder->GetIndexByte(cur - m_AdditionalOffset)];
|
|
COptimal &optimum = m_Optimum[cur + 1];
|
|
if (curAnd1Price < optimum.Price)
|
|
{
|
|
optimum.Price = curAnd1Price;
|
|
optimum.PosPrev = (UInt16)cur;
|
|
}
|
|
if (newLen < kMatchMinLen)
|
|
continue;
|
|
if(cur + newLen > lenEnd)
|
|
{
|
|
if (cur + newLen > kNumOpts - 1)
|
|
newLen = kNumOpts - 1 - cur;
|
|
UInt32 lenEndNew = cur + newLen;
|
|
if (lenEnd < lenEndNew)
|
|
{
|
|
for(UInt32 i = lenEnd + 1; i <= lenEndNew; i++)
|
|
m_Optimum[i].Price = kIfinityPrice;
|
|
lenEnd = lenEndNew;
|
|
}
|
|
}
|
|
for(UInt32 lenTest = kMatchMinLen; lenTest <= newLen; lenTest++)
|
|
{
|
|
UInt16 curBack = m_MatchDistances[lenTest];
|
|
UInt32 curAndLenPrice = curPrice +
|
|
m_LenPrices[lenTest - kMatchMinLen] + m_PosPrices[GetPosSlot(curBack)];
|
|
COptimal &optimum = m_Optimum[cur + lenTest];
|
|
if (curAndLenPrice < optimum.Price)
|
|
{
|
|
optimum.Price = curAndLenPrice;
|
|
optimum.PosPrev = (UInt16)cur;
|
|
optimum.BackPrev = curBack;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CCoder::InitStructures()
|
|
{
|
|
memset(m_LastLevels, 0, kMaxTableSize64);
|
|
|
|
m_ValueIndex = 0;
|
|
m_OptimumEndIndex = 0;
|
|
m_OptimumCurrentIndex = 0;
|
|
m_AdditionalOffset = 0;
|
|
|
|
m_BlockStartPostion = 0;
|
|
m_CurrentBlockUncompressedSize = 0;
|
|
|
|
m_MainCoder.StartNewBlock();
|
|
m_DistCoder.StartNewBlock();
|
|
|
|
UInt32 i;
|
|
for(i = 0; i < 256; i++)
|
|
m_LiteralPrices[i] = 8;
|
|
for(i = 0; i < m_NumLenCombinations; i++)
|
|
m_LenPrices[i] = (Byte)(5 + m_LenDirectBits[g_LenSlots[i]]); // test it
|
|
for(i = 0; i < kDistTableSize64; i++)
|
|
m_PosPrices[i] = (Byte)(5 + kDistDirectBits[i]);
|
|
}
|
|
|
|
void CCoder::WriteBlockData(bool writeMode, bool finalBlock)
|
|
{
|
|
m_MainCoder.AddSymbol(kReadTableNumber);
|
|
int method = WriteTables(writeMode, finalBlock);
|
|
|
|
if (writeMode)
|
|
{
|
|
if(method == NBlockType::kStored)
|
|
{
|
|
for(UInt32 i = 0; i < m_CurrentBlockUncompressedSize; i++)
|
|
{
|
|
Byte b = m_MatchFinder->GetIndexByte(i - m_AdditionalOffset -
|
|
m_CurrentBlockUncompressedSize);
|
|
m_OutStream.WriteBits(b, 8);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (UInt32 i = 0; i < m_ValueIndex; i++)
|
|
{
|
|
if (m_Values[i].Flag == kFlagImm)
|
|
m_MainCoder.CodeOneValue(&m_ReverseOutStream, m_Values[i].Imm);
|
|
else if (m_Values[i].Flag == kFlagLenPos)
|
|
{
|
|
UInt32 len = m_Values[i].Len;
|
|
UInt32 lenSlot = g_LenSlots[len];
|
|
m_MainCoder.CodeOneValue(&m_ReverseOutStream, kMatchNumber + lenSlot);
|
|
m_OutStream.WriteBits(len - m_LenStart[lenSlot], m_LenDirectBits[lenSlot]);
|
|
UInt32 dist = m_Values[i].Pos;
|
|
UInt32 posSlot = GetPosSlot(dist);
|
|
m_DistCoder.CodeOneValue(&m_ReverseOutStream, posSlot);
|
|
m_OutStream.WriteBits(dist - kDistStart[posSlot], kDistDirectBits[posSlot]);
|
|
}
|
|
}
|
|
m_MainCoder.CodeOneValue(&m_ReverseOutStream, kReadTableNumber);
|
|
}
|
|
}
|
|
m_MainCoder.StartNewBlock();
|
|
m_DistCoder.StartNewBlock();
|
|
m_ValueIndex = 0;
|
|
UInt32 i;
|
|
for(i = 0; i < 256; i++)
|
|
if(m_LastLevels[i] != 0)
|
|
m_LiteralPrices[i] = m_LastLevels[i];
|
|
else
|
|
m_LiteralPrices[i] = kNoLiteralDummy;
|
|
|
|
// -------------- Normal match -----------------------------
|
|
|
|
for(i = 0; i < m_NumLenCombinations; i++)
|
|
{
|
|
UInt32 slot = g_LenSlots[i];
|
|
Byte dummy = m_LastLevels[kMatchNumber + slot];
|
|
if (dummy != 0)
|
|
m_LenPrices[i] = dummy;
|
|
else
|
|
m_LenPrices[i] = kNoLenDummy;
|
|
m_LenPrices[i] += m_LenDirectBits[slot];
|
|
}
|
|
for(i = 0; i < kDistTableSize64; i++)
|
|
{
|
|
Byte dummy = m_LastLevels[kDistTableStart + i];
|
|
if (dummy != 0)
|
|
m_PosPrices[i] = dummy;
|
|
else
|
|
m_PosPrices[i] = kNoPosDummy;
|
|
m_PosPrices[i] += kDistDirectBits[i];
|
|
}
|
|
}
|
|
|
|
void CCoder::CodeLevelTable(Byte *newLevels, int numLevels, bool codeMode)
|
|
{
|
|
int prevLen = 0xFF; // last emitted length
|
|
int nextLen = newLevels[0]; // length of next code
|
|
int count = 0; // repeat count of the current code
|
|
int maxCount = 7; // max repeat count
|
|
int minCount = 4; // min repeat count
|
|
if (nextLen == 0)
|
|
{
|
|
maxCount = 138;
|
|
minCount = 3;
|
|
}
|
|
Byte oldValueInGuardElement = newLevels[numLevels]; // push guard value
|
|
try
|
|
{
|
|
newLevels[numLevels] = 0xFF; // guard already set
|
|
for (int n = 0; n < numLevels; n++)
|
|
{
|
|
int curLen = nextLen;
|
|
nextLen = newLevels[n + 1];
|
|
count++;
|
|
if (count < maxCount && curLen == nextLen)
|
|
continue;
|
|
else if (count < minCount)
|
|
for(int i = 0; i < count; i++)
|
|
{
|
|
int codeLen = curLen;
|
|
if (codeMode)
|
|
m_LevelCoder.CodeOneValue(&m_ReverseOutStream, codeLen);
|
|
else
|
|
m_LevelCoder.AddSymbol(codeLen);
|
|
}
|
|
else if (curLen != 0)
|
|
{
|
|
if (curLen != prevLen)
|
|
{
|
|
int codeLen = curLen;
|
|
if (codeMode)
|
|
m_LevelCoder.CodeOneValue(&m_ReverseOutStream, codeLen);
|
|
else
|
|
m_LevelCoder.AddSymbol(codeLen);
|
|
count--;
|
|
}
|
|
if (codeMode)
|
|
{
|
|
m_LevelCoder.CodeOneValue(&m_ReverseOutStream, kTableLevelRepNumber);
|
|
m_OutStream.WriteBits(count - 3, 2);
|
|
}
|
|
else
|
|
m_LevelCoder.AddSymbol(kTableLevelRepNumber);
|
|
}
|
|
else if (count <= 10)
|
|
{
|
|
if (codeMode)
|
|
{
|
|
m_LevelCoder.CodeOneValue(&m_ReverseOutStream, kTableLevel0Number);
|
|
m_OutStream.WriteBits(count - 3, 3);
|
|
}
|
|
else
|
|
m_LevelCoder.AddSymbol(kTableLevel0Number);
|
|
}
|
|
else
|
|
{
|
|
if (codeMode)
|
|
{
|
|
m_LevelCoder.CodeOneValue(&m_ReverseOutStream, kTableLevel0Number2);
|
|
m_OutStream.WriteBits(count - 11, 7);
|
|
}
|
|
else
|
|
m_LevelCoder.AddSymbol(kTableLevel0Number2);
|
|
}
|
|
count = 0;
|
|
prevLen = curLen;
|
|
if (nextLen == 0)
|
|
{
|
|
maxCount = 138;
|
|
minCount = 3;
|
|
}
|
|
else if (curLen == nextLen)
|
|
{
|
|
maxCount = 6;
|
|
minCount = 3;
|
|
}
|
|
else
|
|
{
|
|
maxCount = 7;
|
|
minCount = 4;
|
|
}
|
|
}
|
|
}
|
|
catch(...)
|
|
{
|
|
newLevels[numLevels] = oldValueInGuardElement; // old guard
|
|
throw;
|
|
}
|
|
newLevels[numLevels] = oldValueInGuardElement; // old guard
|
|
}
|
|
|
|
int CCoder::WriteTables(bool writeMode, bool finalBlock)
|
|
{
|
|
Byte newLevels[kMaxTableSize64 + 1]; // (+ 1) for guard
|
|
|
|
m_MainCoder.BuildTree(&newLevels[0]);
|
|
m_DistCoder.BuildTree(&newLevels[kDistTableStart]);
|
|
|
|
|
|
memset(m_LastLevels, 0, kMaxTableSize64);
|
|
|
|
if (writeMode)
|
|
{
|
|
if(finalBlock)
|
|
m_OutStream.WriteBits(NFinalBlockField::kFinalBlock, kFinalBlockFieldSize);
|
|
else
|
|
m_OutStream.WriteBits(NFinalBlockField::kNotFinalBlock, kFinalBlockFieldSize);
|
|
|
|
m_LevelCoder.StartNewBlock();
|
|
|
|
int numLitLenLevels = kMainTableSize;
|
|
while(numLitLenLevels > kDeflateNumberOfLitLenCodesMin && newLevels[numLitLenLevels - 1] == 0)
|
|
numLitLenLevels--;
|
|
|
|
int numDistLevels = _deflate64Mode ? kDistTableSize64 : kDistTableSize32;
|
|
while(numDistLevels > kDeflateNumberOfDistanceCodesMin &&
|
|
newLevels[kDistTableStart + numDistLevels - 1] == 0)
|
|
numDistLevels--;
|
|
|
|
|
|
/////////////////////////
|
|
// First Pass
|
|
|
|
CodeLevelTable(newLevels, numLitLenLevels, false);
|
|
CodeLevelTable(&newLevels[kDistTableStart], numDistLevels, false);
|
|
|
|
memcpy(m_LastLevels, newLevels, kMaxTableSize64);
|
|
|
|
|
|
Byte levelLevels[kLevelTableSize];
|
|
m_LevelCoder.BuildTree(levelLevels);
|
|
|
|
Byte levelLevelsStream[kLevelTableSize];
|
|
int numLevelCodes = kDeflateNumberOfLevelCodesMin;
|
|
int i;
|
|
for (i = 0; i < kLevelTableSize; i++)
|
|
{
|
|
int streamPos = kCodeLengthAlphabetOrder[i];
|
|
Byte level = levelLevels[streamPos];
|
|
if (level > 0 && i >= numLevelCodes)
|
|
numLevelCodes = i + 1;
|
|
levelLevelsStream[i] = level;
|
|
}
|
|
|
|
UInt32 numLZHuffmanBits = m_MainCoder.GetBlockBitLength();
|
|
numLZHuffmanBits += m_DistCoder.GetBlockBitLength();
|
|
numLZHuffmanBits += m_LevelCoder.GetBlockBitLength();
|
|
numLZHuffmanBits += kDeflateNumberOfLengthCodesFieldSize +
|
|
kDeflateNumberOfDistanceCodesFieldSize +
|
|
kDeflateNumberOfLevelCodesFieldSize;
|
|
numLZHuffmanBits += numLevelCodes * kDeflateLevelCodeFieldSize;
|
|
|
|
UInt32 nextBitPosition =
|
|
(m_OutStream.GetBitPosition() + kBlockTypeFieldSize) % 8;
|
|
UInt32 numBitsForAlign = nextBitPosition > 0 ? (8 - nextBitPosition): 0;
|
|
|
|
UInt32 numStoreBits = numBitsForAlign + (2 * 2) * 8;
|
|
numStoreBits += m_CurrentBlockUncompressedSize * 8;
|
|
if(numStoreBits < numLZHuffmanBits)
|
|
{
|
|
m_OutStream.WriteBits(NBlockType::kStored, kBlockTypeFieldSize); // test it
|
|
m_OutStream.WriteBits(0, numBitsForAlign); // test it
|
|
UInt16 currentBlockUncompressedSize = UInt16(m_CurrentBlockUncompressedSize);
|
|
UInt16 currentBlockUncompressedSizeNot = ~currentBlockUncompressedSize;
|
|
m_OutStream.WriteBits(currentBlockUncompressedSize, kDeflateStoredBlockLengthFieldSizeSize);
|
|
m_OutStream.WriteBits(currentBlockUncompressedSizeNot, kDeflateStoredBlockLengthFieldSizeSize);
|
|
return NBlockType::kStored;
|
|
}
|
|
else
|
|
{
|
|
m_OutStream.WriteBits(NBlockType::kDynamicHuffman, kBlockTypeFieldSize);
|
|
m_OutStream.WriteBits(numLitLenLevels - kDeflateNumberOfLitLenCodesMin, kDeflateNumberOfLengthCodesFieldSize);
|
|
m_OutStream.WriteBits(numDistLevels - kDeflateNumberOfDistanceCodesMin,
|
|
kDeflateNumberOfDistanceCodesFieldSize);
|
|
m_OutStream.WriteBits(numLevelCodes - kDeflateNumberOfLevelCodesMin,
|
|
kDeflateNumberOfLevelCodesFieldSize);
|
|
|
|
for (i = 0; i < numLevelCodes; i++)
|
|
m_OutStream.WriteBits(levelLevelsStream[i], kDeflateLevelCodeFieldSize);
|
|
|
|
/////////////////////////
|
|
// Second Pass
|
|
|
|
CodeLevelTable(newLevels, numLitLenLevels, true);
|
|
CodeLevelTable(&newLevels[kDistTableStart], numDistLevels, true);
|
|
return NBlockType::kDynamicHuffman;
|
|
}
|
|
}
|
|
else
|
|
memcpy(m_LastLevels, newLevels, kMaxTableSize64);
|
|
return -1;
|
|
}
|
|
|
|
HRESULT CCoder::CodeReal(ISequentialInStream *inStream,
|
|
ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize,
|
|
ICompressProgressInfo *progress)
|
|
{
|
|
if (!m_Created)
|
|
{
|
|
RINOK(Create());
|
|
|
|
if (!m_MainCoder.Create(kMainTableSize, _deflate64Mode ? kLenDirectBits64 : kLenDirectBits32,
|
|
kMatchNumber, kMaxCodeBitLength))
|
|
return E_OUTOFMEMORY;
|
|
if (!m_DistCoder.Create(_deflate64Mode ? kDistTableSize64 : kDistTableSize32,
|
|
kDistDirectBits, 0, kMaxCodeBitLength))
|
|
return E_OUTOFMEMORY;
|
|
if (!m_LevelCoder.Create(kLevelTableSize, kLevelDirectBits, 0, kMaxLevelBitLength))
|
|
return E_OUTOFMEMORY;
|
|
m_Created = true;
|
|
}
|
|
|
|
UInt64 nowPos = 0;
|
|
m_FinderPos = 0;
|
|
|
|
RINOK(m_MatchFinder->Init(inStream));
|
|
m_OutStream.SetStream(outStream);
|
|
m_OutStream.Init();
|
|
m_ReverseOutStream.Init(&m_OutStream);
|
|
|
|
CCoderReleaser coderReleaser(this);
|
|
InitStructures();
|
|
|
|
while(true)
|
|
{
|
|
int currentPassIndex = 0;
|
|
bool noMoreBytes;
|
|
while (true)
|
|
{
|
|
while(true)
|
|
{
|
|
noMoreBytes = (m_AdditionalOffset == 0 && m_MatchFinder->GetNumAvailableBytes() == 0);
|
|
|
|
if (((m_CurrentBlockUncompressedSize >= kBlockUncompressedSizeThreshold ||
|
|
m_ValueIndex >= kValueBlockSize) &&
|
|
(m_OptimumEndIndex == m_OptimumCurrentIndex))
|
|
|| noMoreBytes)
|
|
break;
|
|
UInt32 pos;
|
|
UInt32 len = GetOptimal(pos);
|
|
if (len >= kMatchMinLen)
|
|
{
|
|
UInt32 newLen = len - kMatchMinLen;
|
|
m_Values[m_ValueIndex].Flag = kFlagLenPos;
|
|
m_Values[m_ValueIndex].Len = Byte(newLen);
|
|
UInt32 lenSlot = g_LenSlots[newLen];
|
|
m_MainCoder.AddSymbol(kMatchNumber + lenSlot);
|
|
m_Values[m_ValueIndex].Pos = UInt16(pos);
|
|
UInt32 posSlot = GetPosSlot(pos);
|
|
m_DistCoder.AddSymbol(posSlot);
|
|
}
|
|
else if (len == 1)
|
|
{
|
|
Byte b = m_MatchFinder->GetIndexByte(0 - m_AdditionalOffset);
|
|
len = 1;
|
|
m_MainCoder.AddSymbol(b);
|
|
m_Values[m_ValueIndex].Flag = kFlagImm;
|
|
m_Values[m_ValueIndex].Imm = b;
|
|
}
|
|
else
|
|
throw 12112342;
|
|
m_ValueIndex++;
|
|
m_AdditionalOffset -= len;
|
|
nowPos += len;
|
|
m_CurrentBlockUncompressedSize += len;
|
|
|
|
}
|
|
currentPassIndex++;
|
|
bool writeMode = (currentPassIndex == m_NumPasses);
|
|
WriteBlockData(writeMode, noMoreBytes);
|
|
if (writeMode)
|
|
break;
|
|
nowPos = m_BlockStartPostion;
|
|
m_AdditionalOffset = UInt32(m_FinderPos - m_BlockStartPostion);
|
|
m_CurrentBlockUncompressedSize = 0;
|
|
}
|
|
m_BlockStartPostion += m_CurrentBlockUncompressedSize;
|
|
m_CurrentBlockUncompressedSize = 0;
|
|
if (progress != NULL)
|
|
{
|
|
UInt64 packSize = m_OutStream.GetProcessedSize();
|
|
RINOK(progress->SetRatioInfo(&nowPos, &packSize));
|
|
}
|
|
if (noMoreBytes)
|
|
break;
|
|
}
|
|
return m_OutStream.Flush();
|
|
}
|
|
|
|
HRESULT CCoder::BaseCode(ISequentialInStream *inStream,
|
|
ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize,
|
|
ICompressProgressInfo *progress)
|
|
{
|
|
try { return CodeReal(inStream, outStream, inSize, outSize, progress); }
|
|
catch(CMatchFinderException &e) { return e.m_Result; }
|
|
catch(const COutBufferException &e) { return e.ErrorCode; }
|
|
catch(...) { return E_FAIL; }
|
|
}
|
|
|
|
STDMETHODIMP CCOMCoder::Code(ISequentialInStream *inStream,
|
|
ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize,
|
|
ICompressProgressInfo *progress)
|
|
{ return BaseCode(inStream, outStream, inSize, outSize, progress); }
|
|
|
|
STDMETHODIMP CCOMCoder::SetCoderProperties(const PROPID *propIDs,
|
|
const PROPVARIANT *properties, UInt32 numProperties)
|
|
{ return BaseSetEncoderProperties2(propIDs, properties, numProperties); }
|
|
|
|
STDMETHODIMP CCOMCoder64::Code(ISequentialInStream *inStream,
|
|
ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize,
|
|
ICompressProgressInfo *progress)
|
|
{ return BaseCode(inStream, outStream, inSize, outSize, progress); }
|
|
|
|
STDMETHODIMP CCOMCoder64::SetCoderProperties(const PROPID *propIDs,
|
|
const PROPVARIANT *properties, UInt32 numProperties)
|
|
{ return BaseSetEncoderProperties2(propIDs, properties, numProperties); }
|
|
|
|
}}}
|