diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs b/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs index 3d2856b0e..bc7ad7a38 100644 --- a/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs +++ b/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// using System; using System.Collections.Generic; using System.Text; @@ -18,106 +17,409 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib /// public class DeflaterHuffman { - private const int BUFSIZE = 1 << (DeflaterConstants.DEFAULT_MEM_LEVEL + 6); - private const int LITERAL_NUM = 286; + private const int BufferSize = 1 << (DeflaterConstants.DEFAULT_MEM_LEVEL + 6); + + // The number of literal codes. + private const int LiteralNumber = 286; // Number of distance codes - private const int DIST_NUM = 30; + private const int DistanceNumber = 30; // Number of codes used to transfer bit lengths - private const int BITLEN_NUM = 19; + private const int BitLengthNumber = 19; // repeat previous bit length 3-6 times (2 bits of repeat count) - private const int REP_3_6 = 16; + private const int Repeat3To6 = 16; // repeat a zero length 3-10 times (3 bits of repeat count) - private const int REP_3_10 = 17; + private const int Repeat3To10 = 17; // repeat a zero length 11-138 times (7 bits of repeat count) - private const int REP_11_138 = 18; + private const int Repeat11To138 = 18; - private const int EOF_SYMBOL = 256; + private const int EofSymbol = 256; // The lengths of the bit length codes are sent in order of decreasing // probability, to avoid transmitting the lengths for unused bit length codes. - private static readonly int[] BL_ORDER = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; - - private static readonly byte[] bit4Reverse = { - 0, - 8, - 4, - 12, - 2, - 10, - 6, - 14, - 1, - 9, - 5, - 13, - 3, - 11, - 7, - 15 - }; + private static readonly int[] BitLengthOrder = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + + private static readonly byte[] Bit4Reverse = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 }; private static short[] staticLCodes; private static byte[] staticLLength; private static short[] staticDCodes; private static byte[] staticDLength; - private class Tree + private Tree literalTree; + private Tree distTree; + private Tree blTree; + + // Buffer for distances + private short[] distanceBuffer; + + private byte[] literalBuffer; + private int lastLiteral; + private int extraBits; + + static DeflaterHuffman() + { + // See RFC 1951 3.2.6 + // Literal codes + staticLCodes = new short[LiteralNumber]; + staticLLength = new byte[LiteralNumber]; + + int i = 0; + while (i < 144) + { + staticLCodes[i] = BitReverse((0x030 + i) << 8); + staticLLength[i++] = 8; + } + + while (i < 256) + { + staticLCodes[i] = BitReverse((0x190 - 144 + i) << 7); + staticLLength[i++] = 9; + } + + while (i < 280) + { + staticLCodes[i] = BitReverse((0x000 - 256 + i) << 9); + staticLLength[i++] = 7; + } + + while (i < LiteralNumber) + { + staticLCodes[i] = BitReverse((0x0c0 - 280 + i) << 8); + staticLLength[i++] = 8; + } + + // Distance codes + staticDCodes = new short[DistanceNumber]; + staticDLength = new byte[DistanceNumber]; + for (i = 0; i < DistanceNumber; i++) + { + staticDCodes[i] = BitReverse(i << 11); + staticDLength[i] = 5; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// Pending buffer to use + public DeflaterHuffman(DeflaterPendingBuffer pending) { - #region Instance Fields + this.Pending = pending; - public short[] freqs; + this.literalTree = new Tree(this, LiteralNumber, 257, 15); + this.distTree = new Tree(this, DistanceNumber, 1, 15); + this.blTree = new Tree(this, BitLengthNumber, 4, 7); - public byte[] length; + this.distanceBuffer = new short[BufferSize]; + this.literalBuffer = new byte[BufferSize]; + } - public int minNumCodes; + /// + /// Gets the pending buffer to use. + /// + public DeflaterPendingBuffer Pending { get; } - public int numCodes; + /// + /// Reset internal state + /// + public void Reset() + { + this.lastLiteral = 0; + this.extraBits = 0; + this.literalTree.Reset(); + this.distTree.Reset(); + this.blTree.Reset(); + } - private short[] codes; - private readonly int[] bl_counts; - private readonly int maxLength; - private DeflaterHuffman dh; + /// + /// Write all trees to pending buffer + /// + /// The number/rank of treecodes to send. + public void SendAllTrees(int blTreeCodes) + { + this.blTree.BuildCodes(); + this.literalTree.BuildCodes(); + this.distTree.BuildCodes(); + this.Pending.WriteBits(this.literalTree.NumCodes - 257, 5); + this.Pending.WriteBits(this.distTree.NumCodes - 1, 5); + this.Pending.WriteBits(blTreeCodes - 4, 4); + for (int rank = 0; rank < blTreeCodes; rank++) + { + this.Pending.WriteBits(this.blTree.Length[BitLengthOrder[rank]], 3); + } + + this.literalTree.WriteTree(this.blTree); + this.distTree.WriteTree(this.blTree); + } + + /// + /// Compress current buffer writing data to pending buffer + /// + public void CompressBlock() + { + for (int i = 0; i < this.lastLiteral; i++) + { + int litlen = this.literalBuffer[i] & 0xff; + int dist = this.distanceBuffer[i]; + if (dist-- != 0) + { + int lc = Lcode(litlen); + this.literalTree.WriteSymbol(lc); + + int bits = (lc - 261) / 4; + if (bits > 0 && bits <= 5) + { + this.Pending.WriteBits(litlen & ((1 << bits) - 1), bits); + } + + int dc = Dcode(dist); + this.distTree.WriteSymbol(dc); + + bits = (dc / 2) - 1; + if (bits > 0) + { + this.Pending.WriteBits(dist & ((1 << bits) - 1), bits); + } + } + else + { + this.literalTree.WriteSymbol(litlen); + } + } + + this.literalTree.WriteSymbol(EofSymbol); + } + + /// + /// Flush block to output with no compression + /// + /// Data to write + /// Index of first byte to write + /// Count of bytes to write + /// True if this is the last block + public void FlushStoredBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock) + { + this.Pending.WriteBits((DeflaterConstants.STORED_BLOCK << 1) + (lastBlock ? 1 : 0), 3); + this.Pending.AlignToByte(); + this.Pending.WriteShort(storedLength); + this.Pending.WriteShort(~storedLength); + this.Pending.WriteBlock(stored, storedOffset, storedLength); + this.Reset(); + } + + /// + /// Flush block to output with compression + /// + /// Data to flush + /// Index of first byte to flush + /// Count of bytes to flush + /// True if this is the last block + public void FlushBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock) + { + this.literalTree.Freqs[EofSymbol]++; + + // Build trees + this.literalTree.BuildTree(); + this.distTree.BuildTree(); + + // Calculate bitlen frequency + this.literalTree.CalcBLFreq(this.blTree); + this.distTree.CalcBLFreq(this.blTree); + + // Build bitlen tree + this.blTree.BuildTree(); + + int blTreeCodes = 4; + for (int i = 18; i > blTreeCodes; i--) + { + if (this.blTree.Length[BitLengthOrder[i]] > 0) + { + blTreeCodes = i + 1; + } + } + + int opt_len = 14 + (blTreeCodes * 3) + this.blTree.GetEncodedLength() + + this.literalTree.GetEncodedLength() + this.distTree.GetEncodedLength() + + this.extraBits; + + int static_len = this.extraBits; + for (int i = 0; i < LiteralNumber; i++) + { + static_len += this.literalTree.Freqs[i] * staticLLength[i]; + } + + for (int i = 0; i < DistanceNumber; i++) + { + static_len += this.distTree.Freqs[i] * staticDLength[i]; + } + + if (opt_len >= static_len) + { + // Force static trees + opt_len = static_len; + } + + if (storedOffset >= 0 && storedLength + 4 < opt_len >> 3) + { + // Store Block + this.FlushStoredBlock(stored, storedOffset, storedLength, lastBlock); + } + else if (opt_len == static_len) + { + // Encode with static tree + this.Pending.WriteBits((DeflaterConstants.STATIC_TREES << 1) + (lastBlock ? 1 : 0), 3); + this.literalTree.SetStaticCodes(staticLCodes, staticLLength); + this.distTree.SetStaticCodes(staticDCodes, staticDLength); + this.CompressBlock(); + this.Reset(); + } + else + { + // Encode with dynamic tree + this.Pending.WriteBits((DeflaterConstants.DYN_TREES << 1) + (lastBlock ? 1 : 0), 3); + this.SendAllTrees(blTreeCodes); + this.CompressBlock(); + this.Reset(); + } + } + + /// + /// Get value indicating if internal buffer is full + /// + /// true if buffer is full + public bool IsFull() + { + return this.lastLiteral >= BufferSize; + } + + /// + /// Add literal to buffer + /// + /// Literal value to add to buffer. + /// Value indicating internal buffer is full + public bool TallyLit(int literal) + { + this.distanceBuffer[this.lastLiteral] = 0; + this.literalBuffer[this.lastLiteral++] = (byte)literal; + this.literalTree.Freqs[literal]++; + return this.IsFull(); + } + + /// + /// Add distance code and length to literal and distance trees + /// + /// Distance code + /// Length + /// Value indicating if internal buffer is full + public bool TallyDist(int distance, int length) + { + this.distanceBuffer[this.lastLiteral] = (short)distance; + this.literalBuffer[this.lastLiteral++] = (byte)(length - 3); + + int lc = Lcode(length - 3); + this.literalTree.Freqs[lc]++; + if (lc >= 265 && lc < 285) + { + this.extraBits += (lc - 261) / 4; + } + + int dc = Dcode(distance - 1); + this.distTree.Freqs[dc]++; + if (dc >= 4) + { + this.extraBits += (dc / 2) - 1; + } + + return this.IsFull(); + } + + /// + /// Reverse the bits of a 16 bit value. + /// + /// Value to reverse bits + /// Value with bits reversed + public static short BitReverse(int toReverse) + { + return (short)(Bit4Reverse[toReverse & 0xF] << 12 | + Bit4Reverse[(toReverse >> 4) & 0xF] << 8 | + Bit4Reverse[(toReverse >> 8) & 0xF] << 4 | + Bit4Reverse[toReverse >> 12]); + } + + private static int Lcode(int length) + { + if (length == 255) + { + return 285; + } + + int code = 257; + while (length >= 8) + { + code += 4; + length >>= 1; + } - #endregion Instance Fields + return code + length; + } - #region Constructors + private static int Dcode(int distance) + { + int code = 0; + while (distance >= 4) + { + code += 2; + distance >>= 1; + } + + return code + distance; + } + + private class Tree + { + private readonly int minNumCodes; + private short[] codes; + private readonly int[] bitLengthCounts; + private readonly int maxLength; + private readonly DeflaterHuffman dh; public Tree(DeflaterHuffman dh, int elems, int minCodes, int maxLength) { this.dh = dh; this.minNumCodes = minCodes; this.maxLength = maxLength; - freqs = new short[elems]; - bl_counts = new int[maxLength]; + this.Freqs = new short[elems]; + this.bitLengthCounts = new int[maxLength]; } - #endregion Constructors + public int NumCodes { get; private set; } + + public short[] Freqs { get; } + + public byte[] Length { get; set; } /// /// Resets the internal state of the tree /// public void Reset() { - for (int i = 0; i < freqs.Length; i++) + for (int i = 0; i < this.Freqs.Length; i++) { - freqs[i] = 0; + this.Freqs[i] = 0; } - codes = null; - length = null; + + this.codes = null; + this.Length = null; } public void WriteSymbol(int code) { - // if (DeflaterConstants.DEBUGGING) { - // freqs[code]--; - // // Console.Write("writeSymbol("+freqs.length+","+code+"): "); - // } - dh.pending.WriteBits(codes[code] & 0xffff, length[code]); + this.dh.Pending.WriteBits(this.codes[code] & 0xffff, this.Length[code]); } /// @@ -129,9 +431,9 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib public void CheckEmpty() { bool empty = true; - for (int i = 0; i < freqs.Length; i++) + for (int i = 0; i < this.Freqs.Length; i++) { - empty &= freqs[i] == 0; + empty &= this.Freqs[i] == 0; } if (!empty) @@ -147,8 +449,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib /// length for new codes public void SetStaticCodes(short[] staticCodes, byte[] staticLengths) { - codes = staticCodes; - length = staticLengths; + this.codes = staticCodes; + this.Length = staticLengths; } /// @@ -156,44 +458,24 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib /// public void BuildCodes() { - int numSymbols = freqs.Length; - int[] nextCode = new int[maxLength]; + int numSymbols = this.Freqs.Length; + int[] nextCode = new int[this.maxLength]; int code = 0; - codes = new short[freqs.Length]; - - // if (DeflaterConstants.DEBUGGING) { - // //Console.WriteLine("buildCodes: "+freqs.Length); - // } + this.codes = new short[this.Freqs.Length]; - for (int bits = 0; bits < maxLength; bits++) + for (int bits = 0; bits < this.maxLength; bits++) { nextCode[bits] = code; - code += bl_counts[bits] << (15 - bits); - - // if (DeflaterConstants.DEBUGGING) { - // //Console.WriteLine("bits: " + ( bits + 1) + " count: " + bl_counts[bits] - // +" nextCode: "+code); - // } + code += this.bitLengthCounts[bits] << (15 - bits); } -#if DebugDeflation - if ( DeflaterConstants.DEBUGGING && (code != 65536) ) - { - throw new ImageFormatException("Inconsistent bl_counts!"); - } -#endif - for (int i = 0; i < numCodes; i++) + for (int i = 0; i < this.NumCodes; i++) { - int bits = length[i]; + int bits = this.Length[i]; if (bits > 0) { - // if (DeflaterConstants.DEBUGGING) { - // //Console.WriteLine("codes["+i+"] = rev(" + nextCode[bits-1]+"), - // +bits); - // } - - codes[i] = BitReverse(nextCode[bits - 1]); + this.codes[i] = BitReverse(nextCode[bits - 1]); nextCode[bits - 1] += 1 << (16 - bits); } } @@ -201,67 +483,65 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib public void BuildTree() { - int numSymbols = freqs.Length; - - /* heap is a priority queue, sorted by frequency, least frequent - * nodes first. The heap is a binary tree, with the property, that - * the parent node is smaller than both child nodes. This assures - * that the smallest node is the first parent. - * - * The binary tree is encoded in an array: 0 is root node and - * the nodes 2*n+1, 2*n+2 are the child nodes of node n. - */ + int numSymbols = this.Freqs.Length; + + // heap is a priority queue, sorted by frequency, least frequent + // nodes first. The heap is a binary tree, with the property, that + // the parent node is smaller than both child nodes. This assures + // that the smallest node is the first parent. + // + // The binary tree is encoded in an array: 0 is root node and + // the nodes 2*n+1, 2*n+2 are the child nodes of node n. int[] heap = new int[numSymbols]; int heapLen = 0; int maxCode = 0; for (int n = 0; n < numSymbols; n++) { - int freq = freqs[n]; + int freq = this.Freqs[n]; if (freq != 0) { // Insert n into heap int pos = heapLen++; int ppos; - while (pos > 0 && freqs[heap[ppos = (pos - 1) / 2]] > freq) + while (pos > 0 && this.Freqs[heap[ppos = (pos - 1) / 2]] > freq) { heap[pos] = heap[ppos]; pos = ppos; } + heap[pos] = n; maxCode = n; } } - /* We could encode a single literal with 0 bits but then we - * don't see the literals. Therefore we force at least two - * literals to avoid this case. We don't care about order in - * this case, both literals get a 1 bit code. - */ + // We could encode a single literal with 0 bits but then we + // don't see the literals. Therefore we force at least two + // literals to avoid this case. We don't care about order in + // this case, both literals get a 1 bit code. while (heapLen < 2) { int node = maxCode < 2 ? ++maxCode : 0; heap[heapLen++] = node; } - numCodes = Math.Max(maxCode + 1, minNumCodes); + this.NumCodes = Math.Max(maxCode + 1, this.minNumCodes); int numLeafs = heapLen; - int[] childs = new int[4 * heapLen - 2]; - int[] values = new int[2 * heapLen - 1]; + int[] childs = new int[(4 * heapLen) - 2]; + int[] values = new int[(2 * heapLen) - 1]; int numNodes = numLeafs; for (int i = 0; i < heapLen; i++) { int node = heap[i]; childs[2 * i] = node; - childs[2 * i + 1] = -1; - values[i] = freqs[node] << 8; + childs[(2 * i) + 1] = -1; + values[i] = this.Freqs[node] << 8; heap[i] = i; } - /* Construct the Huffman tree by repeatedly combining the least two - * frequent nodes. - */ + // Construct the Huffman tree by repeatedly combining the least two + // frequent nodes. do { int first = heap[0]; @@ -280,17 +560,17 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib heap[ppos] = heap[path]; ppos = path; - path = path * 2 + 1; + path = (path * 2) + 1; } - /* Now propagate the last element down along path. Normally - * it shouldn't go too deep. - */ + // Now propagate the last element down along path. Normally + // it shouldn't go too deep. int lastVal = values[last]; while ((path = ppos) > 0 && values[heap[ppos = (path - 1) / 2]] > lastVal) { heap[path] = heap[ppos]; } + heap[path] = last; int second = heap[0]; @@ -298,7 +578,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib // Create a new node father of first and second last = numNodes++; childs[2 * last] = first; - childs[2 * last + 1] = second; + childs[(2 * last) + 1] = second; int mindepth = Math.Min(values[first] & 0xff, values[second] & 0xff); values[last] = lastVal = values[first] + values[second] - mindepth + 1; @@ -315,7 +595,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib heap[ppos] = heap[path]; ppos = path; - path = ppos * 2 + 1; + path = (ppos * 2) + 1; } // Now propagate the new element down along path @@ -323,15 +603,17 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib { heap[path] = heap[ppos]; } + heap[path] = last; - } while (heapLen > 1); + } + while (heapLen > 1); - if (heap[0] != childs.Length / 2 - 1) + if (heap[0] != (childs.Length / 2) - 1) { throw new ImageFormatException("Heap invariant violated"); } - BuildLength(childs); + this.BuildLength(childs); } /// @@ -341,10 +623,11 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib public int GetEncodedLength() { int len = 0; - for (int i = 0; i < freqs.Length; i++) + for (int i = 0; i < this.Freqs.Length; i++) { - len += freqs[i] * length[i]; + len += this.Freqs[i] * this.Length[i]; } + return len; } @@ -360,10 +643,10 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib int curlen = -1; /* length of current code */ int i = 0; - while (i < numCodes) + while (i < this.NumCodes) { count = 1; - int nextlen = length[i]; + int nextlen = this.Length[i]; if (nextlen == 0) { max_count = 138; @@ -375,14 +658,15 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib min_count = 3; if (curlen != nextlen) { - blTree.freqs[nextlen]++; + blTree.Freqs[nextlen]++; count = 0; } } + curlen = nextlen; i++; - while (i < numCodes && curlen == length[i]) + while (i < this.NumCodes && curlen == this.Length[i]) { i++; if (++count >= max_count) @@ -393,19 +677,19 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib if (count < min_count) { - blTree.freqs[curlen] += (short)count; + blTree.Freqs[curlen] += (short)count; } else if (curlen != 0) { - blTree.freqs[REP_3_6]++; + blTree.Freqs[Repeat3To6]++; } else if (count <= 10) { - blTree.freqs[REP_3_10]++; + blTree.Freqs[Repeat3To10]++; } else { - blTree.freqs[REP_11_138]++; + blTree.Freqs[Repeat11To138]++; } } } @@ -422,10 +706,10 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib int curlen = -1; // length of current code int i = 0; - while (i < numCodes) + while (i < this.NumCodes) { count = 1; - int nextlen = length[i]; + int nextlen = this.Length[i]; if (nextlen == 0) { max_count = 138; @@ -441,10 +725,11 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib count = 0; } } + curlen = nextlen; i++; - while (i < numCodes && curlen == length[i]) + while (i < this.NumCodes && curlen == this.Length[i]) { i++; if (++count >= max_count) @@ -462,32 +747,32 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib } else if (curlen != 0) { - blTree.WriteSymbol(REP_3_6); - dh.pending.WriteBits(count - 3, 2); + blTree.WriteSymbol(Repeat3To6); + this.dh.Pending.WriteBits(count - 3, 2); } else if (count <= 10) { - blTree.WriteSymbol(REP_3_10); - dh.pending.WriteBits(count - 3, 3); + blTree.WriteSymbol(Repeat3To10); + this.dh.Pending.WriteBits(count - 3, 3); } else { - blTree.WriteSymbol(REP_11_138); - dh.pending.WriteBits(count - 11, 7); + blTree.WriteSymbol(Repeat11To138); + this.dh.Pending.WriteBits(count - 11, 7); } } } private void BuildLength(int[] childs) { - this.length = new byte[freqs.Length]; + this.Length = new byte[this.Freqs.Length]; int numNodes = childs.Length / 2; int numLeafs = (numNodes + 1) / 2; int overflow = 0; - for (int i = 0; i < maxLength; i++) + for (int i = 0; i < this.maxLength; i++) { - bl_counts[i] = 0; + this.bitLengthCounts[i] = 0; } // First calculate optimal bit lengths @@ -496,43 +781,36 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib for (int i = numNodes - 1; i >= 0; i--) { - if (childs[2 * i + 1] != -1) + if (childs[(2 * i) + 1] != -1) { int bitLength = lengths[i] + 1; - if (bitLength > maxLength) + if (bitLength > this.maxLength) { - bitLength = maxLength; + bitLength = this.maxLength; overflow++; } - lengths[childs[2 * i]] = lengths[childs[2 * i + 1]] = bitLength; + + lengths[childs[2 * i]] = lengths[childs[(2 * i) + 1]] = bitLength; } else { // A leaf node int bitLength = lengths[i]; - bl_counts[bitLength - 1]++; - this.length[childs[2 * i]] = (byte)lengths[i]; + this.bitLengthCounts[bitLength - 1]++; + this.Length[childs[2 * i]] = (byte)lengths[i]; } } - // if (DeflaterConstants.DEBUGGING) { - // //Console.WriteLine("Tree "+freqs.Length+" lengths:"); - // for (int i=0; i < numLeafs; i++) { - // //Console.WriteLine("Node "+childs[2*i]+" freq: "+freqs[childs[2*i]] - // + " len: "+length[childs[2*i]]); - // } - // } - if (overflow == 0) { return; } - int incrBitLen = maxLength - 1; + int incrBitLen = this.maxLength - 1; do { // Find the first bit length which could increase: - while (bl_counts[--incrBitLen] == 0) + while (this.bitLengthCounts[--incrBitLen] == 0) { } @@ -540,426 +818,42 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib // number of overflow nodes. do { - bl_counts[incrBitLen]--; - bl_counts[++incrBitLen]++; - overflow -= 1 << (maxLength - 1 - incrBitLen); - } while (overflow > 0 && incrBitLen < maxLength - 1); - } while (overflow > 0); - - /* We may have overshot above. Move some nodes from maxLength to - * maxLength-1 in that case. - */ - bl_counts[maxLength - 1] += overflow; - bl_counts[maxLength - 2] -= overflow; - - /* Now recompute all bit lengths, scanning in increasing - * frequency. It is simpler to reconstruct all lengths instead of - * fixing only the wrong ones. This idea is taken from 'ar' - * written by Haruhiko Okumura. - * - * The nodes were inserted with decreasing frequency into the childs - * array. - */ + this.bitLengthCounts[incrBitLen]--; + this.bitLengthCounts[++incrBitLen]++; + overflow -= 1 << (this.maxLength - 1 - incrBitLen); + } + while (overflow > 0 && incrBitLen < this.maxLength - 1); + } + while (overflow > 0); + + // We may have overshot above. Move some nodes from maxLength to + // maxLength-1 in that case. + this.bitLengthCounts[this.maxLength - 1] += overflow; + this.bitLengthCounts[this.maxLength - 2] -= overflow; + + // Now recompute all bit lengths, scanning in increasing + // frequency. It is simpler to reconstruct all lengths instead of + // fixing only the wrong ones. This idea is taken from 'ar' + // written by Haruhiko Okumura. + // + // The nodes were inserted with decreasing frequency into the childs + // array. int nodePtr = 2 * numLeafs; - for (int bits = maxLength; bits != 0; bits--) + for (int bits = this.maxLength; bits != 0; bits--) { - int n = bl_counts[bits - 1]; + int n = this.bitLengthCounts[bits - 1]; while (n > 0) { int childPtr = 2 * childs[nodePtr++]; if (childs[childPtr + 1] == -1) { // We found another leaf - length[childs[childPtr]] = (byte)bits; + this.Length[childs[childPtr]] = (byte)bits; n--; } } } - // if (DeflaterConstants.DEBUGGING) { - // //Console.WriteLine("*** After overflow elimination. ***"); - // for (int i=0; i < numLeafs; i++) { - // //Console.WriteLine("Node "+childs[2*i]+" freq: "+freqs[childs[2*i]] - // + " len: "+length[childs[2*i]]); - // } - // } - } - } - - #region Instance Fields - - /// - /// Pending buffer to use - /// - public DeflaterPendingBuffer pending; - - private Tree literalTree; - private Tree distTree; - private Tree blTree; - - // Buffer for distances - private short[] d_buf; - - private byte[] l_buf; - private int last_lit; - private int extra_bits; - - #endregion Instance Fields - - static DeflaterHuffman() - { - // See RFC 1951 3.2.6 - // Literal codes - staticLCodes = new short[LITERAL_NUM]; - staticLLength = new byte[LITERAL_NUM]; - - int i = 0; - while (i < 144) - { - staticLCodes[i] = BitReverse((0x030 + i) << 8); - staticLLength[i++] = 8; - } - - while (i < 256) - { - staticLCodes[i] = BitReverse((0x190 - 144 + i) << 7); - staticLLength[i++] = 9; - } - - while (i < 280) - { - staticLCodes[i] = BitReverse((0x000 - 256 + i) << 9); - staticLLength[i++] = 7; - } - - while (i < LITERAL_NUM) - { - staticLCodes[i] = BitReverse((0x0c0 - 280 + i) << 8); - staticLLength[i++] = 8; - } - - // Distance codes - staticDCodes = new short[DIST_NUM]; - staticDLength = new byte[DIST_NUM]; - for (i = 0; i < DIST_NUM; i++) - { - staticDCodes[i] = BitReverse(i << 11); - staticDLength[i] = 5; - } - } - - /// - /// Construct instance with pending buffer - /// - /// Pending buffer to use - public DeflaterHuffman(DeflaterPendingBuffer pending) - { - this.pending = pending; - - literalTree = new Tree(this, LITERAL_NUM, 257, 15); - distTree = new Tree(this, DIST_NUM, 1, 15); - blTree = new Tree(this, BITLEN_NUM, 4, 7); - - d_buf = new short[BUFSIZE]; - l_buf = new byte[BUFSIZE]; - } - - /// - /// Reset internal state - /// - public void Reset() - { - last_lit = 0; - extra_bits = 0; - literalTree.Reset(); - distTree.Reset(); - blTree.Reset(); - } - - /// - /// Write all trees to pending buffer - /// - /// The number/rank of treecodes to send. - public void SendAllTrees(int blTreeCodes) - { - blTree.BuildCodes(); - literalTree.BuildCodes(); - distTree.BuildCodes(); - pending.WriteBits(literalTree.numCodes - 257, 5); - pending.WriteBits(distTree.numCodes - 1, 5); - pending.WriteBits(blTreeCodes - 4, 4); - for (int rank = 0; rank < blTreeCodes; rank++) - { - pending.WriteBits(blTree.length[BL_ORDER[rank]], 3); - } - literalTree.WriteTree(blTree); - distTree.WriteTree(blTree); - -#if DebugDeflation - if (DeflaterConstants.DEBUGGING) { - blTree.CheckEmpty(); - } -#endif - } - - /// - /// Compress current buffer writing data to pending buffer - /// - public void CompressBlock() - { - for (int i = 0; i < last_lit; i++) - { - int litlen = l_buf[i] & 0xff; - int dist = d_buf[i]; - if (dist-- != 0) - { - // if (DeflaterConstants.DEBUGGING) { - // Console.Write("["+(dist+1)+","+(litlen+3)+"]: "); - // } - - int lc = Lcode(litlen); - literalTree.WriteSymbol(lc); - - int bits = (lc - 261) / 4; - if (bits > 0 && bits <= 5) - { - pending.WriteBits(litlen & ((1 << bits) - 1), bits); - } - - int dc = Dcode(dist); - distTree.WriteSymbol(dc); - - bits = dc / 2 - 1; - if (bits > 0) - { - pending.WriteBits(dist & ((1 << bits) - 1), bits); - } - } - else - { - // if (DeflaterConstants.DEBUGGING) { - // if (litlen > 32 && litlen < 127) { - // Console.Write("("+(char)litlen+"): "); - // } else { - // Console.Write("{"+litlen+"}: "); - // } - // } - literalTree.WriteSymbol(litlen); - } - } - -#if DebugDeflation - if (DeflaterConstants.DEBUGGING) { - Console.Write("EOF: "); - } -#endif - literalTree.WriteSymbol(EOF_SYMBOL); - -#if DebugDeflation - if (DeflaterConstants.DEBUGGING) { - literalTree.CheckEmpty(); - distTree.CheckEmpty(); - } -#endif - } - - /// - /// Flush block to output with no compression - /// - /// Data to write - /// Index of first byte to write - /// Count of bytes to write - /// True if this is the last block - public void FlushStoredBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock) - { -#if DebugDeflation - // if (DeflaterConstants.DEBUGGING) { - // //Console.WriteLine("Flushing stored block "+ storedLength); - // } -#endif - pending.WriteBits((DeflaterConstants.STORED_BLOCK << 1) + (lastBlock ? 1 : 0), 3); - pending.AlignToByte(); - pending.WriteShort(storedLength); - pending.WriteShort(~storedLength); - pending.WriteBlock(stored, storedOffset, storedLength); - Reset(); - } - - /// - /// Flush block to output with compression - /// - /// Data to flush - /// Index of first byte to flush - /// Count of bytes to flush - /// True if this is the last block - public void FlushBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock) - { - literalTree.freqs[EOF_SYMBOL]++; - - // Build trees - literalTree.BuildTree(); - distTree.BuildTree(); - - // Calculate bitlen frequency - literalTree.CalcBLFreq(blTree); - distTree.CalcBLFreq(blTree); - - // Build bitlen tree - blTree.BuildTree(); - - int blTreeCodes = 4; - for (int i = 18; i > blTreeCodes; i--) - { - if (blTree.length[BL_ORDER[i]] > 0) - { - blTreeCodes = i + 1; - } - } - int opt_len = 14 + blTreeCodes * 3 + blTree.GetEncodedLength() + - literalTree.GetEncodedLength() + distTree.GetEncodedLength() + - extra_bits; - - int static_len = extra_bits; - for (int i = 0; i < LITERAL_NUM; i++) - { - static_len += literalTree.freqs[i] * staticLLength[i]; - } - for (int i = 0; i < DIST_NUM; i++) - { - static_len += distTree.freqs[i] * staticDLength[i]; - } - if (opt_len >= static_len) - { - // Force static trees - opt_len = static_len; - } - - if (storedOffset >= 0 && storedLength + 4 < opt_len >> 3) - { - // Store Block - - // if (DeflaterConstants.DEBUGGING) { - // //Console.WriteLine("Storing, since " + storedLength + " < " + opt_len - // + " <= " + static_len); - // } - FlushStoredBlock(stored, storedOffset, storedLength, lastBlock); - } - else if (opt_len == static_len) - { - // Encode with static tree - pending.WriteBits((DeflaterConstants.STATIC_TREES << 1) + (lastBlock ? 1 : 0), 3); - literalTree.SetStaticCodes(staticLCodes, staticLLength); - distTree.SetStaticCodes(staticDCodes, staticDLength); - CompressBlock(); - Reset(); - } - else - { - // Encode with dynamic tree - pending.WriteBits((DeflaterConstants.DYN_TREES << 1) + (lastBlock ? 1 : 0), 3); - SendAllTrees(blTreeCodes); - CompressBlock(); - Reset(); - } - } - - /// - /// Get value indicating if internal buffer is full - /// - /// true if buffer is full - public bool IsFull() - { - return last_lit >= BUFSIZE; - } - - /// - /// Add literal to buffer - /// - /// Literal value to add to buffer. - /// Value indicating internal buffer is full - public bool TallyLit(int literal) - { - // if (DeflaterConstants.DEBUGGING) { - // if (lit > 32 && lit < 127) { - // //Console.WriteLine("("+(char)lit+")"); - // } else { - // //Console.WriteLine("{"+lit+"}"); - // } - // } - d_buf[last_lit] = 0; - l_buf[last_lit++] = (byte)literal; - literalTree.freqs[literal]++; - return IsFull(); - } - - /// - /// Add distance code and length to literal and distance trees - /// - /// Distance code - /// Length - /// Value indicating if internal buffer is full - public bool TallyDist(int distance, int length) - { - // if (DeflaterConstants.DEBUGGING) { - // //Console.WriteLine("[" + distance + "," + length + "]"); - // } - - d_buf[last_lit] = (short)distance; - l_buf[last_lit++] = (byte)(length - 3); - - int lc = Lcode(length - 3); - literalTree.freqs[lc]++; - if (lc >= 265 && lc < 285) - { - extra_bits += (lc - 261) / 4; - } - - int dc = Dcode(distance - 1); - distTree.freqs[dc]++; - if (dc >= 4) - { - extra_bits += dc / 2 - 1; - } - return IsFull(); - } - - /// - /// Reverse the bits of a 16 bit value. - /// - /// Value to reverse bits - /// Value with bits reversed - public static short BitReverse(int toReverse) - { - return (short)(bit4Reverse[toReverse & 0xF] << 12 | - bit4Reverse[(toReverse >> 4) & 0xF] << 8 | - bit4Reverse[(toReverse >> 8) & 0xF] << 4 | - bit4Reverse[toReverse >> 12]); - } - - private static int Lcode(int length) - { - if (length == 255) - { - return 285; } - - int code = 257; - while (length >= 8) - { - code += 4; - length >>= 1; - } - return code + length; - } - - private static int Dcode(int distance) - { - int code = 0; - while (distance >= 4) - { - code += 2; - distance >>= 1; - } - return code + distance; } } }