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