src/programFrames/GzipEncode.cs
|
// C# port of the selected 7-Zip Deflate encoder, compile-time only.
// Copyright (C) 1999-2026 Igor Pavlov; C# adaptation (C) 2026 ps12exe contributors. // SPDX-License-Identifier: LGPL-2.1-or-later // Upstream: https://github.com/ip7z/7zip/tree/0766b733fe3e06dd2a7f9a3cfbf2108ac73abd17 // Adaptations: managed arrays instead of pointers/streams, fixed Deflate32 maximum // profile (9/258/15), portable Huffman sort, no COM, native code or host codec. // LzFind.c and HuffEnc.c are public domain; remaining selected files are LGPL. using System; using System.IO; namespace Ps12exe.Gzip.Internal { internal sealed class DeflateEncoder { const int k_CodeValue_Len_Is_Literal_Flag = 1 << 15; const int kNumDivPassesMax = 10, kNumTables = 1 << 10; const int kFixedHuffmanCodeBlockSizeMax = 256, kDivideCodeBlockSizeMin = 128, kDivideBlockSizeMin = 64; const int kMaxUncompressedBlockSize = 65535, kMatchArraySize = 65535 * 10; const int kMatchArrayLimit = kMatchArraySize - 258 * 4 * 2; const int kNumOptsBase = 4096, kNumOpts = 4096 + 258; const int kBlockUncompressedSizeThreshold = 65535 - 258 - kNumOpts; const int kNoLiteralStatPrice = 11, kNoLenStatPrice = 11, kNoPosStatPrice = 6; const int kIfinityPrice = 0xFFFFFFF, MAX_HUF_LEN_12 = 12, kMaxLevelBitLength = 7; const int kMatchMinLen = 3, kMatchMaxLen = 258; const int kFixedMainTableSize = 288, kFixedDistTableSize = 32, kDistTableSize64 = 32; const int kSymbolEndOfBlock = 256, kSymbolMatch = 257, kMainTableSize = 286; const int kLevelTableSize = 19, kTableDirectLevels = 16; const int kTableLevelRepNumber = 16, kTableLevel0Number = 17, kTableLevel0Number2 = 18; const int kFinalBlockFieldSize = 1, kBlockTypeFieldSize = 2; const int kNumLenCodesFieldSize = 5, kNumDistCodesFieldSize = 5, kNumLevelCodesFieldSize = 4; const int kNumLitLenCodesMin = 257, kNumDistCodesMin = 1, kNumLevelCodesMin = 4; const int kLevelFieldSize = 3, kStoredBlockLengthFieldSize = 16; static readonly int[] kLenStart32 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 255, 0, 0 }; static readonly int[] kLenDirectBits32 = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; static readonly int[] kDistStart = { 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152 }; static readonly int[] kDistDirectBits = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14 }; static readonly int[] kLevelDirectBits = { 2, 3, 7 }; static readonly int[] kCodeLengthAlphabetOrder = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; static readonly int[] g_LenSlots = new int[256], g_FastPos = new int[512]; static DeflateEncoder() { for (int i = 0; i < 29; i++) for (int j = 0; j < (1 << kLenDirectBits32[i]); j++) g_LenSlots[kLenStart32[i] + j] = i; int c = 0; for (int slot = 0; slot < 18; slot++) for (int j = 0; j < (1 << kDistDirectBits[slot]); j++) g_FastPos[c++] = slot; } static int GetPosSlot(int pos) { int shift = pos < 512 ? 0 : 8; return g_FastPos[pos >> shift] + shift * 2; } struct IntSlice { readonly int[] data; readonly int offset; public IntSlice(int[] values, int start) { data = values; offset = start; } public int this[int i] { get { return data[offset + i]; } set { data[offset + i] = value; } } public static implicit operator IntSlice(int[] data) { return new IntSlice(data, 0); } public static IntSlice operator +(IntSlice data, int offset) { return new IntSlice(data.data, data.offset + offset); } } struct CCodeValue { public int Len, Pos; } struct COptimal { public int Price, PosPrev, BackPrev; } class CLevels { public readonly int[] litLenLevels = new int[288], distLevels = new int[32]; public void CopyLevels(CLevels source) { Array.Copy(source.litLenLevels, litLenLevels, 288); Array.Copy(source.distLevels, distLevels, 32); } public void SetFixedLevels() { for (int i = 0; i < 288; i++) litLenLevels[i] = i < 144 ? 8 : i < 256 ? 9 : i < 280 ? 7 : 8; for (int i = 0; i < 32; i++) distLevels[i] = 5; } public void InitStructures() { for (int i = 0; i < 288; i++) litLenLevels[i] = i < 256 ? 8 : i == 256 ? 13 : 5; for (int i = 0; i < 32; i++) distLevels[i] = 5; } } sealed class CTables : CLevels { public bool UseSubBlocks, StoreMode, StaticMode; public int BlockSizeRes, m_Pos; } readonly MatchFinder finder; readonly BitWriter m_OutStream = new BitWriter(); readonly CCodeValue[] m_Values = new CCodeValue[65535]; readonly int[] m_OnePosMatchesMemory = new int[kMatchArraySize]; IntSlice m_MatchDistances; // Upstream normalizes 15 passes to 7 Huffman passes and 10 split levels. const int m_NumFastBytes = 258, m_NumPasses = 7, m_NumDivPasses = 10; readonly bool m_CheckStatic = true; const int m_ValueBlockSize = (7 << 10) + (1 << 12) * m_NumDivPasses; const int m_NumLenCombinations = 256; readonly int[] m_LenStart = kLenStart32, m_LenDirectBits = kLenDirectBits32; int m_Pos, m_NumLitLenLevels, m_NumDistLevels, m_NumLevelCodes, m_ValueIndex; readonly int[] m_LevelLevels = new int[19]; bool m_SecondPass; int m_AdditionalOffset, m_OptimumEndIndex, m_OptimumCurrentIndex, BlockSizeRes; readonly int[] m_LiteralPrices = new int[256], m_LenPrices = new int[256], m_PosPrices = new int[32]; readonly CLevels m_NewLevels = new CLevels(); readonly int[] mainFreqs = new int[288], distFreqs = new int[32]; readonly int[] mainCodes = new int[288], distCodes = new int[32], levelCodes = new int[19], levelLens = new int[19]; readonly CTables[] m_Tables = new CTables[kNumTables]; readonly COptimal[] m_Optimum = new COptimal[kNumOpts]; readonly int[] distanceTmp = new int[kMatchMaxLen * 2 + 3]; DeflateEncoder(byte[] data) { finder = new MatchFinder(data); for (int i = 0; i < m_Tables.Length; i++) m_Tables[i] = new CTables(); } public static byte[] Encode(byte[] data) { if (data == null) throw new ArgumentNullException("data"); return new DeflateEncoder(data).Encode(); } byte[] Encode() { CTables t = m_Tables[1]; t.InitStructures(); do { t.BlockSizeRes = kBlockUncompressedSizeThreshold; m_SecondPass = false; GetBlockPrice(1, m_NumDivPasses); CodeBlock(1, finder.Available == 0); } while (finder.Available != 0); return m_OutStream.Finish(); } void GetMatches() { m_MatchDistances = new IntSlice(m_OnePosMatchesMemory, m_Pos); if (m_SecondPass) { m_Pos += m_MatchDistances[0] + 1; return; } int count = finder.GetMatches(distanceTmp); m_MatchDistances[0] = count; for (int i = 0; i < count; i++) m_MatchDistances[i + 1] = distanceTmp[i]; m_Pos += count + 1; m_AdditionalOffset++; } void MovePos(int num) { if (!m_SecondPass && num > 0) { finder.Skip(num); m_AdditionalOffset += num; } } int Backward(ref int backRes, int cur) { m_OptimumEndIndex = cur; int posMem = m_Optimum[cur].PosPrev; int backMem = m_Optimum[cur].BackPrev; do { int posPrev = posMem; int backCur = backMem; backMem = m_Optimum[posPrev].BackPrev; posMem = m_Optimum[posPrev].PosPrev; m_Optimum[posPrev].BackPrev = backCur; m_Optimum[posPrev].PosPrev = (int)cur; cur = posPrev; } while (cur > 0); backRes = m_Optimum[0].BackPrev; m_OptimumCurrentIndex = m_Optimum[0].PosPrev; return m_OptimumCurrentIndex; } int GetOptimal(ref int backRes) { if (m_OptimumEndIndex != m_OptimumCurrentIndex) { int 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(); int lenEnd; { int numDistancePairs = m_MatchDistances[0]; if (numDistancePairs == 0) return 1; IntSlice matchDistances = m_MatchDistances + 1; lenEnd = matchDistances[numDistancePairs - 2]; if (lenEnd > m_NumFastBytes) { backRes = matchDistances[numDistancePairs - 1]; MovePos(lenEnd - 1); return lenEnd; } m_Optimum[1].Price = m_LiteralPrices[finder.Data[finder.Index - m_AdditionalOffset]]; m_Optimum[1].PosPrev = 0; m_Optimum[2].Price = kIfinityPrice; m_Optimum[2].PosPrev = 1; int offs = 0; for (int i = kMatchMinLen; i <= lenEnd; i++) { int distance = matchDistances[offs + 1]; m_Optimum[i].PosPrev = 0; m_Optimum[i].BackPrev = (int)distance; m_Optimum[i].Price = m_LenPrices[i - kMatchMinLen] + m_PosPrices[GetPosSlot(distance)]; if (i == matchDistances[offs]) offs += 2; } } int cur = 0; for (;;) { ++cur; if (cur == lenEnd || cur == kNumOptsBase || m_Pos >= kMatchArrayLimit) return Backward(ref backRes, cur); GetMatches(); IntSlice matchDistances = m_MatchDistances + 1; int numDistancePairs = m_MatchDistances[0]; int newLen = 0; if (numDistancePairs != 0) { newLen = matchDistances[numDistancePairs - 2]; if (newLen > m_NumFastBytes) { int len = Backward(ref backRes, cur); m_Optimum[cur].BackPrev = matchDistances[numDistancePairs - 1]; m_OptimumEndIndex = cur + newLen; m_Optimum[cur].PosPrev = (int)m_OptimumEndIndex; MovePos(newLen - 1); return len; } } int curPrice = m_Optimum[cur].Price; { int curAnd1Price = curPrice + m_LiteralPrices[finder.Data[finder.Index + cur - m_AdditionalOffset]]; int optimumIndex = cur + 1; if (curAnd1Price < m_Optimum[optimumIndex].Price) { m_Optimum[optimumIndex].Price = curAnd1Price; m_Optimum[optimumIndex].PosPrev = (int)cur; } } if (numDistancePairs == 0) continue; while (lenEnd < cur + newLen) m_Optimum[++lenEnd].Price = kIfinityPrice; int offs = 0; int distance = matchDistances[offs + 1]; curPrice += m_PosPrices[GetPosSlot(distance)]; for (int lenTest = kMatchMinLen;; lenTest++) { int curAndLenPrice = curPrice + m_LenPrices[lenTest - kMatchMinLen]; int optimumIndex = cur + lenTest; if (curAndLenPrice < m_Optimum[optimumIndex].Price) { m_Optimum[optimumIndex].Price = curAndLenPrice; m_Optimum[optimumIndex].PosPrev = (int)cur; m_Optimum[optimumIndex].BackPrev = (int)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 LevelTableDummy(int[] levels, int numLevels, IntSlice 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] += (int)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; minCount = 4; } } } void WriteBits(int value, int numBits) { m_OutStream.WriteBits(value, numBits); } void LevelTableCode(int[] levels, int numLevels, int[] lens, int[] codes) { 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++) WriteBits(codes[curLen], lens[curLen]); else if (curLen != 0) { if (curLen != prevLen) { WriteBits(codes[curLen], lens[curLen]); count--; } WriteBits(codes[kTableLevelRepNumber], lens[kTableLevelRepNumber]); WriteBits(count - 3, 2); } else if (count <= 10) { WriteBits(codes[kTableLevel0Number], lens[kTableLevel0Number]); WriteBits(count - 3, 3); } else { WriteBits(codes[kTableLevel0Number2], lens[kTableLevel0Number2]); WriteBits(count - 11, 7); } count = 0; prevLen = curLen; if (nextLen == 0) { maxCount = 138; minCount = 3; } else if (curLen == nextLen) { maxCount = 6; minCount = 3; } else { maxCount = 7; minCount = 4; } } } void MakeTables(int maxHuffLen) { Huffman_Generate(mainFreqs, mainCodes, m_NewLevels.litLenLevels, kFixedMainTableSize, maxHuffLen); Huffman_Generate(distFreqs, distCodes, m_NewLevels.distLevels, kDistTableSize64, maxHuffLen); } static int Huffman_GetPrice(IntSlice freqs, IntSlice lens, int num) { int price = 0; int i; for (i = 0; i < num; i++) price += lens[i] * freqs[i]; return price; } static int Huffman_GetPrice_Spec(IntSlice freqs, int[] lens, int num, IntSlice extraBits, int extraBase) { return Huffman_GetPrice(freqs, lens, num) + Huffman_GetPrice(freqs + extraBase, extraBits, num - extraBase); } int GetLzBlockPrice() { return Huffman_GetPrice_Spec(mainFreqs, m_NewLevels.litLenLevels, kFixedMainTableSize, m_LenDirectBits, kSymbolMatch) + Huffman_GetPrice_Spec(distFreqs, m_NewLevels.distLevels, kDistTableSize64, kDistDirectBits, 0); } void TryBlock() { Array.Clear(mainFreqs, 0, mainFreqs.Length); Array.Clear(distFreqs, 0, distFreqs.Length); m_ValueIndex = 0; int blockSize = BlockSizeRes; BlockSizeRes = 0; for (;;) { if (m_OptimumCurrentIndex == m_OptimumEndIndex) { if (m_Pos >= kMatchArrayLimit || BlockSizeRes >= blockSize || (!m_SecondPass && ((finder.Available == 0) || m_ValueIndex >= m_ValueBlockSize))) break; } int pos = 0; int len; len = GetOptimal(ref pos); int codeValueIndex = m_ValueIndex++; if (len >= kMatchMinLen) { int newLen = len - kMatchMinLen; m_Values[codeValueIndex].Len = (int)newLen; mainFreqs[kSymbolMatch + g_LenSlots[newLen]]++; m_Values[codeValueIndex].Pos = (int)pos; distFreqs[GetPosSlot(pos)]++; } else { int b = finder.Data[finder.Index - m_AdditionalOffset]; mainFreqs[b]++; m_Values[codeValueIndex].Len = k_CodeValue_Len_Is_Literal_Flag; m_Values[codeValueIndex].Pos = (int)b; } m_AdditionalOffset -= len; BlockSizeRes += len; } mainFreqs[kSymbolEndOfBlock]++; m_AdditionalOffset += BlockSizeRes; m_SecondPass = true; } void SetPrices(CLevels levels) { int i; for (i = 0; i < 256; i++) { int price = levels.litLenLevels[i]; m_LiteralPrices[i] = ((price != 0) ? price : kNoLiteralStatPrice); } for (i = 0; i < m_NumLenCombinations; i++) { int slot = g_LenSlots[i]; int price = levels.litLenLevels[kSymbolMatch + slot]; m_LenPrices[i] = (int)(((price != 0) ? price : kNoLenStatPrice) + m_LenDirectBits[slot]); } for (i = 0; i < kDistTableSize64; i++) { int price = levels.distLevels[i]; m_PosPrices[i] = (int)(((price != 0) ? price : kNoPosStatPrice) + kDistDirectBits[i]); } } static void Huffman_ReverseBits(int[] codes, int[] lens, int num) { for (int i = 0; i < num; i++) { int x = codes[i], reverse = 0; for (int bit = 0; bit < lens[i]; bit++) { reverse = (reverse << 1) | (x & 1); x >>= 1; } codes[i] = reverse; } } void WriteBlock() { Huffman_ReverseBits(mainCodes, m_NewLevels.litLenLevels, kFixedMainTableSize); Huffman_ReverseBits(distCodes, m_NewLevels.distLevels, kDistTableSize64); for (int i = 0; i < m_ValueIndex; i++) { int len = m_Values[i].Len, dist = m_Values[i].Pos; if (len == k_CodeValue_Len_Is_Literal_Flag) WriteBits(mainCodes[dist], m_NewLevels.litLenLevels[dist]); else { int slot = g_LenSlots[len]; WriteBits(mainCodes[kSymbolMatch + slot], m_NewLevels.litLenLevels[kSymbolMatch + slot]); WriteBits(len - m_LenStart[slot], m_LenDirectBits[slot]); int posSlot = GetPosSlot(dist); WriteBits(distCodes[posSlot], m_NewLevels.distLevels[posSlot]); WriteBits(dist - kDistStart[posSlot], kDistDirectBits[posSlot]); } } WriteBits(mainCodes[kSymbolEndOfBlock], m_NewLevels.litLenLevels[kSymbolEndOfBlock]); } static int GetStorePrice(int blockSize, int bitPosition) { int price = 0; do { int nextBitPosition = (bitPosition + kFinalBlockFieldSize + kBlockTypeFieldSize) & 7; int numBitsForAlign = nextBitPosition > 0 ? (8 - nextBitPosition) : 0; int curBlockSize = (blockSize < (1 << 16)) ? blockSize : (1 << 16) - 1; price += kFinalBlockFieldSize + kBlockTypeFieldSize + numBitsForAlign + (2 + 2) * 8 + curBlockSize * 8; bitPosition = 0; blockSize -= curBlockSize; } while (blockSize != 0); return price; } void WriteStoreBlock(int blockSize, int additionalOffset, bool finalBlock) { do { int curBlockSize = (blockSize < (1 << 16)) ? blockSize : (1 << 16) - 1; blockSize -= curBlockSize; WriteBits((finalBlock && (blockSize == 0) ? 1 : 0), kFinalBlockFieldSize); WriteBits(0, kBlockTypeFieldSize); m_OutStream.FlushByte(); WriteBits((int)curBlockSize, kStoredBlockLengthFieldSize); WriteBits((int)~curBlockSize, kStoredBlockLengthFieldSize); int dataIndex = finder.Index - additionalOffset; for (int i = 0; i < curBlockSize; i++) m_OutStream.WriteByte(finder.Data[dataIndex + i]); additionalOffset -= curBlockSize; } while (blockSize != 0); } int TryDynBlock(int tableIndex, int numPasses) { CTables t = m_Tables[tableIndex]; BlockSizeRes = t.BlockSizeRes; int posTemp = t.m_Pos; SetPrices(t); for (int p = 0; p < numPasses; p++) { m_Pos = posTemp; TryBlock(); int numHuffBits = m_ValueIndex > 18000 ? MAX_HUF_LEN_12 : m_ValueIndex > 7000 ? 11 : m_ValueIndex > 2000 ? 10 : 9; MakeTables(numHuffBits); SetPrices(m_NewLevels); } t.CopyLevels(m_NewLevels); m_NumLitLenLevels = kMainTableSize; while (m_NumLitLenLevels > kNumLitLenCodesMin && m_NewLevels.litLenLevels[m_NumLitLenLevels - 1] == 0) m_NumLitLenLevels--; m_NumDistLevels = kDistTableSize64; while (m_NumDistLevels > kNumDistCodesMin && m_NewLevels.distLevels[m_NumDistLevels - 1] == 0) m_NumDistLevels--; int[] levelFreqs = new int[kLevelTableSize]; LevelTableDummy(m_NewLevels.litLenLevels, m_NumLitLenLevels, levelFreqs); LevelTableDummy(m_NewLevels.distLevels, m_NumDistLevels, levelFreqs); Huffman_Generate(levelFreqs, levelCodes, levelLens, kLevelTableSize, kMaxLevelBitLength); m_NumLevelCodes = kNumLevelCodesMin; for (int i = 0; i < kLevelTableSize; i++) { int level = levelLens[kCodeLengthAlphabetOrder[i]]; if (level > 0 && i >= m_NumLevelCodes) m_NumLevelCodes = i + 1; m_LevelLevels[i] = level; } return GetLzBlockPrice() + Huffman_GetPrice_Spec(levelFreqs, levelLens, kLevelTableSize, kLevelDirectBits, kTableDirectLevels) + kNumLenCodesFieldSize + kNumDistCodesFieldSize + kNumLevelCodesFieldSize + m_NumLevelCodes * kLevelFieldSize + kFinalBlockFieldSize + kBlockTypeFieldSize; } int TryFixedBlock(int tableIndex) { CTables t = m_Tables[tableIndex]; BlockSizeRes = t.BlockSizeRes; m_Pos = t.m_Pos; m_NewLevels.SetFixedLevels(); SetPrices(m_NewLevels); TryBlock(); return kFinalBlockFieldSize + kBlockTypeFieldSize + GetLzBlockPrice(); } int GetBlockPrice(int tableIndex, int numDivPasses) { CTables t = m_Tables[tableIndex]; t.StaticMode = false; int price = TryDynBlock(tableIndex, m_NumPasses); t.BlockSizeRes = BlockSizeRes; int numValues = m_ValueIndex; int posTemp = m_Pos; int additionalOffsetEnd = m_AdditionalOffset; if (m_CheckStatic && m_ValueIndex <= kFixedHuffmanCodeBlockSizeMax) { int fixedPrice = TryFixedBlock(tableIndex); t.StaticMode = (fixedPrice < price); if (t.StaticMode) price = fixedPrice; } int storePrice = GetStorePrice(BlockSizeRes, 0); t.StoreMode = (storePrice <= price); if (t.StoreMode) price = storePrice; t.UseSubBlocks = false; if (numDivPasses > 1 && numValues >= kDivideCodeBlockSizeMin) { CTables t0 = m_Tables[(tableIndex << 1)]; t0.CopyLevels(t); t0.BlockSizeRes = t.BlockSizeRes >> 1; t0.m_Pos = t.m_Pos; int subPrice = GetBlockPrice((tableIndex << 1), numDivPasses - 1); int blockSize2 = t.BlockSizeRes - t0.BlockSizeRes; if (t0.BlockSizeRes >= kDivideBlockSizeMin && blockSize2 >= kDivideBlockSizeMin) { CTables t1 = m_Tables[(tableIndex << 1) + 1]; t1.CopyLevels(t); t1.BlockSizeRes = blockSize2; t1.m_Pos = m_Pos; m_AdditionalOffset -= t0.BlockSizeRes; subPrice += GetBlockPrice((tableIndex << 1) + 1, numDivPasses - 1); t.UseSubBlocks = (subPrice < price); if (t.UseSubBlocks) price = subPrice; } } m_AdditionalOffset = additionalOffsetEnd; m_Pos = posTemp; return price; } void CodeBlock(int tableIndex, bool finalBlock) { CTables t = m_Tables[tableIndex]; if (t.UseSubBlocks) { CodeBlock((tableIndex << 1), false); CodeBlock((tableIndex << 1) + 1, finalBlock); } else { if (t.StoreMode) WriteStoreBlock(t.BlockSizeRes, m_AdditionalOffset, finalBlock); else { WriteBits((finalBlock ? 1 : 0), kFinalBlockFieldSize); if (t.StaticMode) { WriteBits(1, kBlockTypeFieldSize); TryFixedBlock(tableIndex); int i; int kMaxStaticHuffLen = 9; for (i = 0; i < kFixedMainTableSize; i++) mainFreqs[i] = (int)1 << (kMaxStaticHuffLen - m_NewLevels.litLenLevels[i]); for (i = 0; i < kFixedDistTableSize; i++) distFreqs[i] = (int)1 << (kMaxStaticHuffLen - m_NewLevels.distLevels[i]); MakeTables(kMaxStaticHuffLen); } else { if (m_NumDivPasses > 1 || m_CheckStatic) TryDynBlock(tableIndex, 1); WriteBits(2, kBlockTypeFieldSize); WriteBits(m_NumLitLenLevels - kNumLitLenCodesMin, kNumLenCodesFieldSize); WriteBits(m_NumDistLevels - kNumDistCodesMin, kNumDistCodesFieldSize); WriteBits(m_NumLevelCodes - kNumLevelCodesMin, kNumLevelCodesFieldSize); for (int i = 0; i < m_NumLevelCodes; i++) WriteBits(m_LevelLevels[i], kLevelFieldSize); Huffman_ReverseBits(levelCodes, levelLens, kLevelTableSize); LevelTableCode(m_NewLevels.litLenLevels, m_NumLitLenLevels, levelLens, levelCodes); LevelTableCode(m_NewLevels.distLevels, m_NumDistLevels, levelLens, levelCodes); } WriteBlock(); } m_AdditionalOffset -= t.BlockSizeRes; } } // Portable form of 7-Zip's public-domain HuffEnc.c. Packed frequencies // retain symbol ordering; equal-frequency leaves precede internal nodes. static void Huffman_Generate(IntSlice freqs, int[] p, int[] lens, int numSymbols, int maxLen) { const int MASK = 1023, FREQ_MASK = ~1023; Array.Clear(lens, 0, lens.Length); int num = 0; for (int i = 0; i < numSymbols; i++) if (freqs[i] != 0) p[num++] = i | (freqs[i] << 10); Array.Sort(p, 0, num); if (num <= 2) { int minCode = 0, maxCode = 1; if (num != 0) { maxCode = p[num - 1] & MASK; if (num == 2) { minCode = p[0] & MASK; if (minCode > maxCode) { int temp = minCode; minCode = maxCode; maxCode = temp; } } else if (maxCode == 0) maxCode++; } p[minCode] = 0; p[maxCode] = 1; lens[minCode] = lens[maxCode] = 1; return; } int[] lenCounters = new int[17]; lenCounters[1] = 2; int fb = (p[1] & FREQ_MASK) + p[0], f = p[2] & FREQ_MASK; int pi = 2, e = 0, b = 0, n = num; for (;;) { int sum; e++; if (fb < f) { sum = fb & FREQ_MASK; p[b++] = (fb & MASK) | (e << 10); fb = p[b]; if (b == e) { if (++pi == n) break; sum += f; fb = (fb & MASK) | sum; p[e] = fb; f = p[pi] & FREQ_MASK; continue; } } else if (++pi == n) { p[b++] = (fb & MASK) | (e << 10); break; } else { sum = f; f = p[pi] & FREQ_MASK; } if (fb < f) { sum = (sum + fb) & FREQ_MASK; p[b++] = (fb & MASK) | (e << 10); p[e] = (p[e] & MASK) | sum; fb = p[b]; } else if (++pi == n) break; else { sum += f; f = p[pi] & FREQ_MASK; p[e] = (p[e] & MASK) | sum; } } n -= 2; p[n] &= MASK; if (n != b) { int parent = n; do { int len = (p[parent] >> 10) + 1; parent--; lenCounters[len] -= 2; lenCounters[len + 1] += 4; n -= 2; p[n] = (p[n] & MASK) | (len << 10); p[n + 1] = (p[n + 1] & MASK) | (len << 10); } while (n != b); } while (b != 0) { b--; int len = (p[p[b] >> 10] >> 10) + 1; p[b] = (p[b] & MASK) | (len << 10); if (len >= maxLen) { for (len = maxLen - 1; lenCounters[len] == 0; len--) { } } lenCounters[len]--; lenCounters[len + 1] += 2; } int sorted = 0; for (int len = maxLen; len != 0; len--) for (int k = 0; k < lenCounters[len]; k++) lens[p[sorted++] & MASK] = len; int[] codes = new int[17]; int code = 0; for (int len = 0; len < 16; len++) codes[len + 1] = code = (code + lenCounters[len]) << 1; for (int i = 0; i < numSymbols; i++) { int len = lens[i]; p[i] = codes[len]++; } } // Public-domain Bt3Zip match finder from LzFind.c, specialized to a complete // byte array, 32 KiB history, 258-byte matches and 145 tree search cycles. sealed class MatchFinder { const int Window = 32769; public readonly byte[] Data; public int Index; public int Available { get { return Data.Length - Index; } } readonly int[] hash = new int[65536], son = new int[Window * 2]; static readonly uint[] crc = CreateCrc(); static uint[] CreateCrc() { uint[] values = new uint[256]; for (uint i = 0; i < 256; i++) { uint r = i; for (int bit = 0; bit < 8; bit++) r = (r >> 1) ^ ((r & 1) != 0 ? 0xedb88320U : 0); values[i] = r; } return values; } public MatchFinder(byte[] data) { Data = data; } public int GetMatches(int[] distances) { return Find(distances); } public void Skip(int count) { while (count-- != 0) Find(null); } int Find(int[] distances) { int limit = Math.Min(258, Available); if (limit < 3) { Index++; return 0; } int pos = Index + 1, cyclic = pos % Window; int hv = (int)(((uint)Data[Index + 2] | ((uint)Data[Index] << 8)) ^ crc[Data[Index + 1]]) & 65535; int curMatch = hash[hv]; hash[hv] = pos; int ptr0 = cyclic * 2 + 1, ptr1 = cyclic * 2; int len0 = 0, len1 = 0, maxLen = 2, count = 0; int minPosition = Math.Max(0, pos - Window), cycles = 145; while (curMatch > minPosition) { int delta = pos - curMatch; int pair = (cyclic - delta + (cyclic < delta ? Window : 0)) * 2; int previous = Index - delta, len = Math.Min(len0, len1); int pair0 = son[pair]; if (Data[previous + len] == Data[Index + len]) { while (++len != limit && Data[previous + len] == Data[Index + len]) { } if (maxLen < len) { maxLen = len; if (distances != null) { distances[count++] = len; distances[count++] = delta - 1; } if (len == limit) { son[ptr1] = pair0; son[ptr0] = son[pair + 1]; Index++; return count; } } } if (Data[previous + len] < Data[Index + len]) { son[ptr1] = curMatch; curMatch = son[pair + 1]; ptr1 = pair + 1; len1 = len; } else { son[ptr0] = curMatch; curMatch = pair0; ptr0 = pair; len0 = len; } if (--cycles == 0) break; } son[ptr0] = son[ptr1] = 0; Index++; return count; } } sealed class BitWriter { readonly MemoryStream output = new MemoryStream(); uint bits; int count; public void WriteBits(int value, int length) { bits |= ((uint)value & ((1U << length) - 1)) << count; count += length; while (count >= 8) { output.WriteByte((byte)bits); bits >>= 8; count -= 8; } } public void FlushByte() { if (count != 0) output.WriteByte((byte)bits); bits = 0; count = 0; } public void WriteByte(byte value) { output.WriteByte(value); } public byte[] Finish() { FlushByte(); byte[] result = output.ToArray(); output.Dispose(); return result; } } } } |