1 namespace System.IO.Compression { 2 using System; 3 using System.Diagnostics; 4 using System.Globalization; 5 6 internal class FastEncoder { 7 8 private FastEncoderWindow inputWindow; // input history window 9 private Match currentMatch; // current match in history window 10 private double lastCompressionRatio; 11 FastEncoder()12 public FastEncoder() { 13 inputWindow = new FastEncoderWindow(); 14 currentMatch = new Match(); 15 } 16 17 internal int BytesInHistory { 18 get { 19 return inputWindow.BytesAvailable; 20 } 21 } 22 23 internal DeflateInput UnprocessedInput { 24 get { 25 return inputWindow.UnprocessedInput; 26 } 27 } 28 FlushInput()29 internal void FlushInput() { 30 inputWindow.FlushWindow(); 31 } 32 33 internal Double LastCompressionRatio { 34 get { return lastCompressionRatio; } 35 } 36 37 // Copy the compressed bytes to output buffer as a block. maxBytesToCopy limits the number of 38 // bytes we can copy from input. Set to any value < 1 if no limit GetBlock(DeflateInput input, OutputBuffer output, int maxBytesToCopy)39 internal void GetBlock(DeflateInput input, OutputBuffer output, int maxBytesToCopy) { 40 Debug.Assert(InputAvailable(input), "call SetInput before trying to compress!"); 41 42 WriteDeflatePreamble(output); 43 GetCompressedOutput(input, output, maxBytesToCopy); 44 WriteEndOfBlock(output); 45 } 46 47 // Compress data but don't format as block (doesn't have header and footer) GetCompressedData(DeflateInput input, OutputBuffer output)48 internal void GetCompressedData(DeflateInput input, OutputBuffer output) { 49 GetCompressedOutput(input, output, -1); 50 } 51 GetBlockHeader(OutputBuffer output)52 internal void GetBlockHeader(OutputBuffer output) { 53 WriteDeflatePreamble(output); 54 } 55 GetBlockFooter(OutputBuffer output)56 internal void GetBlockFooter(OutputBuffer output) { 57 WriteEndOfBlock(output); 58 } 59 60 // maxBytesToCopy limits the number of bytes we can copy from input. Set to any value < 1 if no limit GetCompressedOutput(DeflateInput input, OutputBuffer output, int maxBytesToCopy)61 private void GetCompressedOutput(DeflateInput input, OutputBuffer output, int maxBytesToCopy) { 62 // snapshot for compression ratio stats 63 int bytesWrittenPre = output.BytesWritten; 64 int bytesConsumedFromInput = 0; 65 int inputBytesPre = BytesInHistory + input.Count; 66 67 do { 68 // read more input data into the window if there is space available 69 int bytesToCopy = (input.Count < inputWindow.FreeWindowSpace) ? 70 input.Count : inputWindow.FreeWindowSpace; 71 if (maxBytesToCopy >= 1) { 72 bytesToCopy = Math.Min(bytesToCopy, maxBytesToCopy - bytesConsumedFromInput); 73 } 74 if (bytesToCopy > 0) { 75 // copy data into history window 76 inputWindow.CopyBytes(input.Buffer, input.StartIndex, bytesToCopy); 77 input.ConsumeBytes(bytesToCopy); 78 bytesConsumedFromInput += bytesToCopy; 79 } 80 81 GetCompressedOutput(output); 82 83 } while (SafeToWriteTo(output) && InputAvailable(input) && (maxBytesToCopy < 1 || bytesConsumedFromInput < maxBytesToCopy)); 84 85 // determine compression ratio, save 86 int bytesWrittenPost = output.BytesWritten; 87 int bytesWritten = bytesWrittenPost - bytesWrittenPre; 88 int inputBytesPost = BytesInHistory + input.Count; 89 int totalBytesConsumed = inputBytesPre - inputBytesPost; 90 if (bytesWritten != 0) { 91 lastCompressionRatio = (double)bytesWritten / (double)totalBytesConsumed; 92 } 93 94 } 95 96 // compress the bytes in input history window GetCompressedOutput(OutputBuffer output)97 private void GetCompressedOutput(OutputBuffer output) { 98 99 while (inputWindow.BytesAvailable > 0 && SafeToWriteTo(output)) { 100 101 // Find next match. A match can be a symbol, 102 // a distance/length pair, a symbol followed by a distance/Length pair 103 inputWindow.GetNextSymbolOrMatch(currentMatch); 104 105 if (currentMatch.State == MatchState.HasSymbol) { 106 WriteChar(currentMatch.Symbol, output); 107 } 108 else if (currentMatch.State == MatchState.HasMatch) { 109 WriteMatch(currentMatch.Length, currentMatch.Position, output); 110 } 111 else { 112 WriteChar(currentMatch.Symbol, output); 113 WriteMatch(currentMatch.Length, currentMatch.Position, output); 114 } 115 } 116 } 117 InputAvailable(DeflateInput input)118 private bool InputAvailable(DeflateInput input) { 119 return input.Count > 0 || BytesInHistory > 0; 120 } 121 SafeToWriteTo(OutputBuffer output)122 private bool SafeToWriteTo(OutputBuffer output) { // can we safely continue writing to output buffer 123 return output.FreeBytes > FastEncoderStatics.MaxCodeLen; 124 } 125 WriteEndOfBlock(OutputBuffer output)126 private void WriteEndOfBlock(OutputBuffer output) { 127 // The fast encoder outputs one long block, so it just needs to terminate this block 128 const int EndOfBlockCode = 256; 129 uint code_info = FastEncoderStatics.FastEncoderLiteralCodeInfo[EndOfBlockCode]; 130 int code_len = (int)(code_info & 31); 131 output.WriteBits(code_len, code_info >> 5); 132 } 133 WriteMatch(int matchLen, int matchPos, OutputBuffer output)134 static internal void WriteMatch(int matchLen, int matchPos, OutputBuffer output) { 135 Debug.Assert(matchLen >= FastEncoderWindow.MinMatch && matchLen <= FastEncoderWindow.MaxMatch, "Illegal currentMatch length!"); 136 Debug.WriteLineIf(CompressionTracingSwitch.Verbose, String.Format(CultureInfo.InvariantCulture, "Match: {0}:{1}", matchLen, matchPos), "Compression"); 137 138 // Get the code information for a match code 139 uint codeInfo = FastEncoderStatics.FastEncoderLiteralCodeInfo[(FastEncoderStatics.NumChars + 1 - FastEncoderWindow.MinMatch) + matchLen]; 140 int codeLen = (int)codeInfo & 31; 141 Debug.Assert(codeLen != 0, "Invalid Match Length!"); 142 if (codeLen <= 16) { 143 output.WriteBits(codeLen, codeInfo >> 5); 144 } 145 else { 146 output.WriteBits(16, (codeInfo >> 5) & 65535); 147 output.WriteBits(codeLen - 16, codeInfo >> (5 + 16)); 148 } 149 150 // Get the code information for a distance code 151 codeInfo = FastEncoderStatics.FastEncoderDistanceCodeInfo[FastEncoderStatics.GetSlot(matchPos)]; 152 output.WriteBits((int)(codeInfo & 15), codeInfo >> 8); 153 int extraBits = (int)(codeInfo >> 4) & 15; 154 if (extraBits != 0) { 155 output.WriteBits(extraBits, (uint)matchPos & FastEncoderStatics.BitMask[extraBits]); 156 } 157 } 158 WriteChar(byte b, OutputBuffer output)159 static internal void WriteChar(byte b, OutputBuffer output) { 160 Debug.WriteLineIf(CompressionTracingSwitch.Verbose, String.Format(CultureInfo.InvariantCulture, "Literal: {0}", b ), "Compression"); 161 162 uint code = FastEncoderStatics.FastEncoderLiteralCodeInfo[b]; 163 output.WriteBits((int)code & 31, code >> 5); 164 } 165 166 // Output the block type and tree structure for our hard-coded trees. 167 // Contains following data: 168 // "final" block flag 1 bit 169 // BLOCKTYPE_DYNAMIC 2 bits 170 // FastEncoderLiteralTreeLength 171 // FastEncoderDistanceTreeLength 172 // WriteDeflatePreamble(OutputBuffer output)173 static internal void WriteDeflatePreamble(OutputBuffer output) { 174 //Debug.Assert( bitCount == 0, "bitCount must be zero before writing tree bit!"); 175 176 output.WriteBytes(FastEncoderStatics.FastEncoderTreeStructureData, 0, FastEncoderStatics.FastEncoderTreeStructureData.Length); 177 output.WriteBits(FastEncoderStatics.FastEncoderPostTreeBitCount, FastEncoderStatics.FastEncoderPostTreeBitBuf); 178 } 179 180 } 181 182 } 183