deflateencoder.cpp
来自「由7-zip提供的压缩、解压缩程序」· C++ 代码 · 共 882 行 · 第 1/2 页
CPP
882 行
// DeflateEncoder.cpp#include "StdAfx.h"#include "DeflateEncoder.h"#include "Windows/Defs.h"#include "Common/ComTry.h"#include "../../../Common/Alloc.h"#include "../LZ/BinTree/BinTree3Z.h"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 << 6); // [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 = 15;static const int kMaxLevelBitLength = 7;struct CMatchFinderException{ HRESULT ErrorCode; CMatchFinderException(HRESULT errorCode): ErrorCode(errorCode) {}};static Byte kNoLiteralStatPrice = 13;static Byte kNoLenStatPrice = 13;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;}CCoder::CCoder(bool deflate64Mode): m_Deflate64Mode(deflate64Mode), m_NumPasses(1), m_NumDivPasses(1), m_NumFastBytes(32), 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;}HRESULT CCoder::Create(){ COM_TRY_BEGIN if (!m_MatchFinder) { NBT3Z::CMatchFinder *matchFinderSpec = new NBT3Z::CMatchFinder; m_SetMfPasses = matchFinderSpec; m_MatchFinder = matchFinderSpec; if (m_MatchFinder == 0) return E_OUTOFMEMORY; } 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) { RINOK(m_MatchFinder->Create(m_Deflate64Mode ? kHistorySize64 : kHistorySize32, kNumOpts + kMaxUncompressedBlockSize, m_NumFastBytes, m_MatchMaxLen - m_NumFastBytes)); if (!m_OutStream.Create(1 << 20)) return E_OUTOFMEMORY; if (!MainCoder.Create(kFixedMainTableSize, m_LenDirectBits, kSymbolMatch, kMaxCodeBitLength)) return E_OUTOFMEMORY; if (!DistCoder.Create(kDistTableSize64, kDistDirectBits, 0, kMaxCodeBitLength)) return E_OUTOFMEMORY; if (!LevelCoder.Create(kLevelTableSize, kLevelDirectBits, kTableDirectLevels, kMaxLevelBitLength)) return E_OUTOFMEMORY; } if (m_MatchFinderCycles != 0 && m_SetMfPasses != 0) m_SetMfPasses->SetNumPasses(m_MatchFinderCycles); m_Created = true; return S_OK; COM_TRY_END}// ICompressSetEncoderProperties2HRESULT 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; } 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();}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]; HRESULT result = m_MatchFinder->GetMatches(distanceTmp); if (result != S_OK) throw CMatchFinderException(result); UInt32 numPairs = distanceTmp[0]; *m_MatchDistances = (UInt16)numPairs; if (numPairs > 0) { UInt32 i = 1; for(i = 1; i < numPairs; i += 2) { m_MatchDistances[i] = (UInt16)distanceTmp[i]; m_MatchDistances[i + 1] = (UInt16)distanceTmp[i + 1]; } UInt32 len = distanceTmp[1 + numPairs - 2]; if (len == m_NumFastBytes && m_NumFastBytes != m_MatchMaxLen) m_MatchDistances[i - 2] = (UInt16)(len + m_MatchFinder->GetMatchLen(len - 1, distanceTmp[1 + numPairs - 1], m_MatchMaxLen - len)); } if (m_IsMultiPass) m_Pos += numPairs + 1; if (!m_SecondPass) m_AdditionalOffset++;}void CCoder::MovePos(UInt32 num){ if (!m_SecondPass && num > 0) { HRESULT result = m_MatchFinder->Skip(num); if (result != S_OK) throw CMatchFinderException(result); m_AdditionalOffset += num; }}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 = 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[m_MatchFinder->GetIndexByte(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 = 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; while(true) { ++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_Optimum[cur].PosPrev = m_OptimumEndIndex = cur + newLen; MovePos(newLen - 1); return len; } } 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(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 = 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)]; } } }}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;}void CCoder::CodeLevelTable(NStream::NLSBF::CEncoder *outStream, const Byte *levels, int numLevels){ 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) for(int i = 0; i < count; i++) if (outStream != 0) LevelCoder.CodeOneValue(outStream, curLen);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?