root / trunk / hammock / src / net35 / ICSharpCode.SharpZipLib.Silverlight / Zip / Compression / DeflaterEngine.cs @ 0eea575a
History | View | Annotate | Download (25.9 kB)
1 |
// DeflaterEngine.cs |
---|---|
2 |
// |
3 |
// Copyright (C) 2001 Mike Krueger |
4 |
// Copyright (C) 2004 John Reilly |
5 |
// |
6 |
// This file was translated from java, it was part of the GNU Classpath |
7 |
// Copyright (C) 2001 Free Software Foundation, Inc. |
8 |
// |
9 |
// This program is free software; you can redistribute it and/or |
10 |
// modify it under the terms of the GNU General Public License |
11 |
// as published by the Free Software Foundation; either version 2 |
12 |
// of the License, or (at your option) any later version. |
13 |
// |
14 |
// This program is distributed in the hope that it will be useful, |
15 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 |
// GNU General Public License for more details. |
18 |
// |
19 |
// You should have received a copy of the GNU General Public License |
20 |
// along with this program; if not, write to the Free Software |
21 |
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
22 |
// |
23 |
// Linking this library statically or dynamically with other modules is |
24 |
// making a combined work based on this library. Thus, the terms and |
25 |
// conditions of the GNU General Public License cover the whole |
26 |
// combination. |
27 |
// |
28 |
// As a special exception, the copyright holders of this library give you |
29 |
// permission to link this library with independent modules to produce an |
30 |
// executable, regardless of the license terms of these independent |
31 |
// modules, and to copy and distribute the resulting executable under |
32 |
// terms of your choice, provided that you also meet, for each linked |
33 |
// independent module, the terms and conditions of the license of that |
34 |
// module. An independent module is a module which is not derived from |
35 |
// or based on this library. If you modify this library, you may extend |
36 |
// this exception to your version of the library, but you are not |
37 |
// obligated to do so. If you do not wish to do so, delete this |
38 |
// exception statement from your version. |
39 |
|
40 |
using System; |
41 |
using ICSharpCode.SharpZipLib.Silverlight.Checksums; |
42 |
using ICSharpCode.SharpZipLib.Silverlight.Zip.Compression; |
43 |
|
44 |
namespace ICSharpCode.SharpZipLib.Silverlight.Zip.Compression |
45 |
{ |
46 |
/// <summary> |
47 |
/// Strategies for deflater |
48 |
/// </summary> |
49 |
public enum DeflateStrategy |
50 |
{ |
51 |
/// <summary> |
52 |
/// The default strategy |
53 |
/// </summary> |
54 |
Default = 0, |
55 |
|
56 |
/// <summary> |
57 |
/// This strategy will only allow longer string repetitions. It is |
58 |
/// useful for random data with a small character set. |
59 |
/// </summary> |
60 |
Filtered = 1, |
61 |
|
62 |
|
63 |
/// <summary> |
64 |
/// This strategy will not look for string repetitions at all. It |
65 |
/// only encodes with Huffman trees (which means, that more common |
66 |
/// characters get a smaller encoding. |
67 |
/// </summary> |
68 |
HuffmanOnly = 2 |
69 |
} |
70 |
|
71 |
/// <summary> |
72 |
/// Low level compression engine for deflate algorithm which uses a 32K sliding window |
73 |
/// with secondary compression from Huffman/Shannon-Fano codes. |
74 |
/// </summary> |
75 |
public class DeflaterEngine : DeflaterConstants |
76 |
{ |
77 |
#region Constants |
78 |
const int TooFar = 4096; |
79 |
#endregion |
80 |
|
81 |
#region Constructors |
82 |
/// <summary> |
83 |
/// Construct instance with pending buffer |
84 |
/// </summary> |
85 |
/// <param name="pending"> |
86 |
/// Pending buffer to use |
87 |
/// </param>> |
88 |
public DeflaterEngine(DeflaterPending pending) |
89 |
{ |
90 |
this.pending = pending; |
91 |
huffman = new DeflaterHuffman(pending); |
92 |
adler = new Adler32(); |
93 |
|
94 |
window = new byte[2 * WSIZE]; |
95 |
head = new short[HASH_SIZE]; |
96 |
prev = new short[WSIZE]; |
97 |
|
98 |
// We start at index 1, to avoid an implementation deficiency, that |
99 |
// we cannot build a repeat pattern at index 0. |
100 |
blockStart = strstart = 1; |
101 |
} |
102 |
|
103 |
#endregion |
104 |
|
105 |
/// <summary> |
106 |
/// Deflate drives actual compression of data |
107 |
/// </summary> |
108 |
/// <param name="flush">True to flush input buffers</param> |
109 |
/// <param name="finish">Finish deflation with the current input.</param> |
110 |
/// <returns>Returns true if progress has been made.</returns> |
111 |
public bool Deflate(bool flush, bool finish) |
112 |
{ |
113 |
bool progress; |
114 |
do |
115 |
{ |
116 |
FillWindow(); |
117 |
var canFlush = flush && (inputOff == inputEnd); |
118 |
switch (compressionFunction) |
119 |
{ |
120 |
case DEFLATE_STORED: |
121 |
progress = DeflateStored(canFlush, finish); |
122 |
break; |
123 |
case DEFLATE_FAST: |
124 |
progress = DeflateFast(canFlush, finish); |
125 |
break; |
126 |
case DEFLATE_SLOW: |
127 |
progress = DeflateSlow(canFlush, finish); |
128 |
break; |
129 |
default: |
130 |
throw new InvalidOperationException("unknown compressionFunction"); |
131 |
} |
132 |
} while (pending.IsFlushed && progress); // repeat while we have no pending output and progress was made |
133 |
return progress; |
134 |
} |
135 |
|
136 |
/// <summary> |
137 |
/// Sets input data to be deflated. Should only be called when <code>NeedsInput()</code> |
138 |
/// returns true |
139 |
/// </summary> |
140 |
/// <param name="buffer">The buffer containing input data.</param> |
141 |
/// <param name="offset">The offset of the first byte of data.</param> |
142 |
/// <param name="count">The number of bytes of data to use as input.</param> |
143 |
public void SetInput(byte[] buffer, int offset, int count) |
144 |
{ |
145 |
if ( buffer == null ) |
146 |
{ |
147 |
throw new ArgumentNullException("buffer"); |
148 |
} |
149 |
|
150 |
if ( offset < 0 ) |
151 |
{ |
152 |
throw new ArgumentOutOfRangeException("offset"); |
153 |
} |
154 |
|
155 |
if ( count < 0 ) |
156 |
{ |
157 |
throw new ArgumentOutOfRangeException("count"); |
158 |
} |
159 |
|
160 |
if (inputOff < inputEnd) |
161 |
{ |
162 |
throw new InvalidOperationException("Old input was not completely processed"); |
163 |
} |
164 |
|
165 |
int end = offset + count; |
166 |
|
167 |
/* We want to throw an ArrayIndexOutOfBoundsException early. The |
168 |
* check is very tricky: it also handles integer wrap around. |
169 |
*/ |
170 |
if ((offset > end) || (end > buffer.Length) ) |
171 |
{ |
172 |
throw new ArgumentOutOfRangeException("count"); |
173 |
} |
174 |
|
175 |
inputBuf = buffer; |
176 |
inputOff = offset; |
177 |
inputEnd = end; |
178 |
} |
179 |
|
180 |
/// <summary> |
181 |
/// Determines if more <see cref="SetInput">input</see> is needed. |
182 |
/// </summary> |
183 |
/// <returns>Return true if input is needed via <see cref="SetInput">SetInput</see></returns> |
184 |
public bool NeedsInput() |
185 |
{ |
186 |
return (inputEnd == inputOff); |
187 |
} |
188 |
|
189 |
/// <summary> |
190 |
/// Set compression dictionary |
191 |
/// </summary> |
192 |
/// <param name="buffer">The buffer containing the dictionary data</param> |
193 |
/// <param name="offset">The offset in the buffer for the first byte of data</param> |
194 |
/// <param name="length">The length of the dictionary data.</param> |
195 |
public void SetDictionary(byte[] buffer, int offset, int length) |
196 |
{ |
197 |
adler.Update(buffer, offset, length); |
198 |
if (length < MIN_MATCH) |
199 |
{ |
200 |
return; |
201 |
} |
202 |
|
203 |
if (length > MAX_DIST) |
204 |
{ |
205 |
offset += length - MAX_DIST; |
206 |
length = MAX_DIST; |
207 |
} |
208 |
|
209 |
Array.Copy(buffer, offset, window, strstart, length); |
210 |
|
211 |
UpdateHash(); |
212 |
--length; |
213 |
while (--length > 0) |
214 |
{ |
215 |
InsertString(); |
216 |
strstart++; |
217 |
} |
218 |
strstart += 2; |
219 |
blockStart = strstart; |
220 |
} |
221 |
|
222 |
/// <summary> |
223 |
/// Reset internal state |
224 |
/// </summary> |
225 |
public void Reset() |
226 |
{ |
227 |
huffman.Reset(); |
228 |
adler.Reset(); |
229 |
blockStart = strstart = 1; |
230 |
lookahead = 0; |
231 |
totalIn = 0; |
232 |
prevAvailable = false; |
233 |
matchLen = MIN_MATCH - 1; |
234 |
|
235 |
for (int i = 0; i < HASH_SIZE; i++) { |
236 |
head[i] = 0; |
237 |
} |
238 |
|
239 |
for (int i = 0; i < WSIZE; i++) { |
240 |
prev[i] = 0; |
241 |
} |
242 |
} |
243 |
|
244 |
/// <summary> |
245 |
/// Reset Adler checksum |
246 |
/// </summary> |
247 |
public void ResetAdler() |
248 |
{ |
249 |
adler.Reset(); |
250 |
} |
251 |
|
252 |
/// <summary> |
253 |
/// Get current value of Adler checksum |
254 |
/// </summary> |
255 |
public int Adler { |
256 |
get { |
257 |
return unchecked((int)adler.Value); |
258 |
} |
259 |
} |
260 |
|
261 |
/// <summary> |
262 |
/// Total data processed |
263 |
/// </summary> |
264 |
public int TotalIn { |
265 |
get { |
266 |
return totalIn; |
267 |
} |
268 |
} |
269 |
|
270 |
/// <summary> |
271 |
/// Get/set the <see cref="DeflateStrategy">deflate strategy</see> |
272 |
/// </summary> |
273 |
public DeflateStrategy Strategy { |
274 |
get { |
275 |
return strategy; |
276 |
} |
277 |
set { |
278 |
strategy = value; |
279 |
} |
280 |
} |
281 |
|
282 |
/// <summary> |
283 |
/// Set the deflate level (0-9) |
284 |
/// </summary> |
285 |
/// <param name="level">The value to set the level to.</param> |
286 |
public void SetLevel(int level) |
287 |
{ |
288 |
if ( (level < 0) || (level > 9) ) |
289 |
{ |
290 |
throw new ArgumentOutOfRangeException("level"); |
291 |
} |
292 |
|
293 |
goodLength = GOOD_LENGTH[level]; |
294 |
max_lazy = MAX_LAZY[level]; |
295 |
niceLength = NICE_LENGTH[level]; |
296 |
max_chain = MAX_CHAIN[level]; |
297 |
|
298 |
if (COMPR_FUNC[level] == compressionFunction) |
299 |
{ |
300 |
return; |
301 |
} |
302 |
|
303 |
switch (compressionFunction) { |
304 |
case DEFLATE_STORED: |
305 |
if (strstart > blockStart) { |
306 |
huffman.FlushStoredBlock(window, blockStart, |
307 |
strstart - blockStart, false); |
308 |
blockStart = strstart; |
309 |
} |
310 |
UpdateHash(); |
311 |
break; |
312 |
|
313 |
case DEFLATE_FAST: |
314 |
if (strstart > blockStart) { |
315 |
huffman.FlushBlock(window, blockStart, strstart - blockStart, |
316 |
false); |
317 |
blockStart = strstart; |
318 |
} |
319 |
break; |
320 |
|
321 |
case DEFLATE_SLOW: |
322 |
if (prevAvailable) { |
323 |
huffman.TallyLit(window[strstart-1] & 0xff); |
324 |
} |
325 |
if (strstart > blockStart) { |
326 |
huffman.FlushBlock(window, blockStart, strstart - blockStart, false); |
327 |
blockStart = strstart; |
328 |
} |
329 |
prevAvailable = false; |
330 |
matchLen = MIN_MATCH - 1; |
331 |
break; |
332 |
} |
333 |
compressionFunction = COMPR_FUNC[level]; |
334 |
} |
335 |
|
336 |
/// <summary> |
337 |
/// Fill the window |
338 |
/// </summary> |
339 |
public void FillWindow() |
340 |
{ |
341 |
/* If the window is almost full and there is insufficient lookahead, |
342 |
* move the upper half to the lower one to make room in the upper half. |
343 |
*/ |
344 |
if (strstart >= WSIZE + MAX_DIST) |
345 |
{ |
346 |
SlideWindow(); |
347 |
} |
348 |
|
349 |
/* If there is not enough lookahead, but still some input left, |
350 |
* read in the input |
351 |
*/ |
352 |
while (lookahead < MIN_LOOKAHEAD && inputOff < inputEnd) |
353 |
{ |
354 |
int more = 2 * WSIZE - lookahead - strstart; |
355 |
|
356 |
if (more > inputEnd - inputOff) |
357 |
{ |
358 |
more = inputEnd - inputOff; |
359 |
} |
360 |
|
361 |
Array.Copy(inputBuf, inputOff, window, strstart + lookahead, more); |
362 |
adler.Update(inputBuf, inputOff, more); |
363 |
|
364 |
inputOff += more; |
365 |
totalIn += more; |
366 |
lookahead += more; |
367 |
} |
368 |
|
369 |
if (lookahead >= MIN_MATCH) |
370 |
{ |
371 |
UpdateHash(); |
372 |
} |
373 |
} |
374 |
|
375 |
void UpdateHash() |
376 |
{ |
377 |
/* |
378 |
if (DEBUGGING) { |
379 |
Console.WriteLine("updateHash: "+strstart); |
380 |
} |
381 |
*/ |
382 |
ins_h = (window[strstart] << HASH_SHIFT) ^ window[strstart + 1]; |
383 |
} |
384 |
|
385 |
/// <summary> |
386 |
/// Inserts the current string in the head hash and returns the previous |
387 |
/// value for this hash. |
388 |
/// </summary> |
389 |
/// <returns>The previous hash value</returns> |
390 |
int InsertString() |
391 |
{ |
392 |
short match; |
393 |
var hash = ((ins_h << HASH_SHIFT) ^ window[strstart + (MIN_MATCH -1)]) & HASH_MASK; |
394 |
prev[strstart & WMASK] = match = head[hash]; |
395 |
head[hash] = unchecked((short)strstart); |
396 |
ins_h = hash; |
397 |
return match & 0xffff; |
398 |
} |
399 |
|
400 |
void SlideWindow() |
401 |
{ |
402 |
Array.Copy(window, WSIZE, window, 0, WSIZE); |
403 |
matchStart -= WSIZE; |
404 |
strstart -= WSIZE; |
405 |
blockStart -= WSIZE; |
406 |
|
407 |
// Slide the hash table (could be avoided with 32 bit values |
408 |
// at the expense of memory usage). |
409 |
for (int i = 0; i < HASH_SIZE; ++i) { |
410 |
int m = head[i] & 0xffff; |
411 |
head[i] = (short)(m >= WSIZE ? (m - WSIZE) : 0); |
412 |
} |
413 |
|
414 |
// Slide the prev table. |
415 |
for (int i = 0; i < WSIZE; i++) { |
416 |
int m = prev[i] & 0xffff; |
417 |
prev[i] = (short)(m >= WSIZE ? (m - WSIZE) : 0); |
418 |
} |
419 |
} |
420 |
|
421 |
/// <summary> |
422 |
/// Find the best (longest) string in the window matching the |
423 |
/// string starting at strstart. |
424 |
/// |
425 |
/// Preconditions: |
426 |
/// <code> |
427 |
/// strstart + MAX_MATCH <= window.length.</code> |
428 |
/// </summary> |
429 |
/// <param name="curMatch"></param> |
430 |
/// <returns>True if a match greater than the minimum length is found</returns> |
431 |
bool FindLongestMatch(int curMatch) |
432 |
{ |
433 |
var chainLength = max_chain; |
434 |
var niceLength = this.niceLength; |
435 |
var prev = this.prev; |
436 |
var scan = strstart; |
437 |
var best_end = strstart + matchLen; |
438 |
var best_len = Math.Max(matchLen, MIN_MATCH - 1); |
439 |
|
440 |
int limit = Math.Max(strstart - MAX_DIST, 0); |
441 |
|
442 |
int strend = strstart + MAX_MATCH - 1; |
443 |
byte scan_end1 = window[best_end - 1]; |
444 |
byte scan_end = window[best_end]; |
445 |
|
446 |
// Do not waste too much time if we already have a good match: |
447 |
if (best_len >= goodLength) { |
448 |
chainLength >>= 2; |
449 |
} |
450 |
|
451 |
/* Do not look for matches beyond the end of the input. This is necessary |
452 |
* to make deflate deterministic. |
453 |
*/ |
454 |
if (niceLength > lookahead) { |
455 |
niceLength = lookahead; |
456 |
} |
457 |
|
458 |
do { |
459 |
if (window[curMatch + best_len] != scan_end || |
460 |
window[curMatch + best_len - 1] != scan_end1 || |
461 |
window[curMatch] != window[scan] || |
462 |
window[curMatch + 1] != window[scan + 1]) { |
463 |
continue; |
464 |
} |
465 |
|
466 |
int match = curMatch + 2; |
467 |
scan += 2; |
468 |
|
469 |
/* We check for insufficient lookahead only every 8th comparison; |
470 |
* the 256th check will be made at strstart + 258. |
471 |
*/ |
472 |
while ( |
473 |
window[++scan] == window[++match] && |
474 |
window[++scan] == window[++match] && |
475 |
window[++scan] == window[++match] && |
476 |
window[++scan] == window[++match] && |
477 |
window[++scan] == window[++match] && |
478 |
window[++scan] == window[++match] && |
479 |
window[++scan] == window[++match] && |
480 |
window[++scan] == window[++match] && |
481 |
(scan < strend)) |
482 |
{ |
483 |
// Do nothing |
484 |
} |
485 |
|
486 |
if (scan > best_end) { |
487 |
matchStart = curMatch; |
488 |
best_end = scan; |
489 |
best_len = scan - strstart; |
490 |
|
491 |
if (best_len >= niceLength) { |
492 |
break; |
493 |
} |
494 |
|
495 |
scan_end1 = window[best_end - 1]; |
496 |
scan_end = window[best_end]; |
497 |
} |
498 |
scan = strstart; |
499 |
} while ((curMatch = (prev[curMatch & WMASK] & 0xffff)) > limit && --chainLength != 0); |
500 |
|
501 |
matchLen = Math.Min(best_len, lookahead); |
502 |
return matchLen >= MIN_MATCH; |
503 |
} |
504 |
|
505 |
bool DeflateStored(bool flush, bool finish) |
506 |
{ |
507 |
if (!flush && (lookahead == 0)) { |
508 |
return false; |
509 |
} |
510 |
|
511 |
strstart += lookahead; |
512 |
lookahead = 0; |
513 |
|
514 |
int storedLength = strstart - blockStart; |
515 |
|
516 |
if ((storedLength >= MAX_BLOCK_SIZE) || // Block is full |
517 |
(blockStart < WSIZE && storedLength >= MAX_DIST) || // Block may move out of window |
518 |
flush) { |
519 |
bool lastBlock = finish; |
520 |
if (storedLength > MAX_BLOCK_SIZE) { |
521 |
storedLength = MAX_BLOCK_SIZE; |
522 |
lastBlock = false; |
523 |
} |
524 |
|
525 |
huffman.FlushStoredBlock(window, blockStart, storedLength, lastBlock); |
526 |
blockStart += storedLength; |
527 |
return !lastBlock; |
528 |
} |
529 |
return true; |
530 |
} |
531 |
|
532 |
bool DeflateFast(bool flush, bool finish) |
533 |
{ |
534 |
if (lookahead < MIN_LOOKAHEAD && !flush) { |
535 |
return false; |
536 |
} |
537 |
|
538 |
while (lookahead >= MIN_LOOKAHEAD || flush) { |
539 |
if (lookahead == 0) { |
540 |
// We are flushing everything |
541 |
huffman.FlushBlock(window, blockStart, strstart - blockStart, finish); |
542 |
blockStart = strstart; |
543 |
return false; |
544 |
} |
545 |
|
546 |
if (strstart > 2 * WSIZE - MIN_LOOKAHEAD) { |
547 |
/* slide window, as FindLongestMatch needs this. |
548 |
* This should only happen when flushing and the window |
549 |
* is almost full. |
550 |
*/ |
551 |
SlideWindow(); |
552 |
} |
553 |
|
554 |
int hashHead; |
555 |
if (lookahead >= MIN_MATCH && |
556 |
(hashHead = InsertString()) != 0 && |
557 |
strategy != DeflateStrategy.HuffmanOnly && |
558 |
strstart - hashHead <= MAX_DIST && |
559 |
FindLongestMatch(hashHead)) { |
560 |
// longestMatch sets matchStart and matchLen |
561 |
var full = huffman.TallyDist(strstart - matchStart, matchLen); |
562 |
|
563 |
lookahead -= matchLen; |
564 |
if (matchLen <= max_lazy && lookahead >= MIN_MATCH) { |
565 |
while (--matchLen > 0) { |
566 |
++strstart; |
567 |
InsertString(); |
568 |
} |
569 |
++strstart; |
570 |
} else { |
571 |
strstart += matchLen; |
572 |
if (lookahead >= MIN_MATCH - 1) { |
573 |
UpdateHash(); |
574 |
} |
575 |
} |
576 |
matchLen = MIN_MATCH - 1; |
577 |
if (!full) { |
578 |
continue; |
579 |
} |
580 |
} else { |
581 |
// No match found |
582 |
huffman.TallyLit(window[strstart] & 0xff); |
583 |
++strstart; |
584 |
--lookahead; |
585 |
} |
586 |
|
587 |
if (huffman.IsFull()) { |
588 |
bool lastBlock = finish && (lookahead == 0); |
589 |
huffman.FlushBlock(window, blockStart, strstart - blockStart, lastBlock); |
590 |
blockStart = strstart; |
591 |
return !lastBlock; |
592 |
} |
593 |
} |
594 |
return true; |
595 |
} |
596 |
|
597 |
bool DeflateSlow(bool flush, bool finish) |
598 |
{ |
599 |
if (lookahead < MIN_LOOKAHEAD && !flush) { |
600 |
return false; |
601 |
} |
602 |
|
603 |
while (lookahead >= MIN_LOOKAHEAD || flush) { |
604 |
if (lookahead == 0) { |
605 |
if (prevAvailable) { |
606 |
huffman.TallyLit(window[strstart-1] & 0xff); |
607 |
} |
608 |
prevAvailable = false; |
609 |
|
610 |
// We are flushing everything |
611 |
huffman.FlushBlock(window, blockStart, strstart - blockStart, |
612 |
finish); |
613 |
blockStart = strstart; |
614 |
return false; |
615 |
} |
616 |
|
617 |
if (strstart >= 2 * WSIZE - MIN_LOOKAHEAD) { |
618 |
/* slide window, as FindLongestMatch needs this. |
619 |
* This should only happen when flushing and the window |
620 |
* is almost full. |
621 |
*/ |
622 |
SlideWindow(); |
623 |
} |
624 |
|
625 |
int prevMatch = matchStart; |
626 |
int prevLen = matchLen; |
627 |
if (lookahead >= MIN_MATCH) { |
628 |
|
629 |
int hashHead = InsertString(); |
630 |
|
631 |
if (strategy != DeflateStrategy.HuffmanOnly && |
632 |
hashHead != 0 && |
633 |
strstart - hashHead <= MAX_DIST && |
634 |
FindLongestMatch(hashHead)) { |
635 |
|
636 |
// longestMatch sets matchStart and matchLen |
637 |
|
638 |
// Discard match if too small and too far away |
639 |
if (matchLen <= 5 && (strategy == DeflateStrategy.Filtered || (matchLen == MIN_MATCH && strstart - matchStart > TooFar))) { |
640 |
matchLen = MIN_MATCH - 1; |
641 |
} |
642 |
} |
643 |
} |
644 |
|
645 |
// previous match was better |
646 |
if ((prevLen >= MIN_MATCH) && (matchLen <= prevLen) ) { |
647 |
huffman.TallyDist(strstart - 1 - prevMatch, prevLen); |
648 |
prevLen -= 2; |
649 |
do { |
650 |
strstart++; |
651 |
lookahead--; |
652 |
if (lookahead >= MIN_MATCH) { |
653 |
InsertString(); |
654 |
} |
655 |
} while (--prevLen > 0); |
656 |
|
657 |
strstart ++; |
658 |
lookahead--; |
659 |
prevAvailable = false; |
660 |
matchLen = MIN_MATCH - 1; |
661 |
} else { |
662 |
if (prevAvailable) { |
663 |
huffman.TallyLit(window[strstart-1] & 0xff); |
664 |
} |
665 |
prevAvailable = true; |
666 |
strstart++; |
667 |
lookahead--; |
668 |
} |
669 |
|
670 |
if (huffman.IsFull()) { |
671 |
int len = strstart - blockStart; |
672 |
if (prevAvailable) { |
673 |
len--; |
674 |
} |
675 |
bool lastBlock = (finish && (lookahead == 0) && !prevAvailable); |
676 |
huffman.FlushBlock(window, blockStart, len, lastBlock); |
677 |
blockStart += len; |
678 |
return !lastBlock; |
679 |
} |
680 |
} |
681 |
return true; |
682 |
} |
683 |
|
684 |
#region Instance Fields |
685 |
|
686 |
// Hash index of string to be inserted |
687 |
int ins_h; |
688 |
|
689 |
/// <summary> |
690 |
/// Hashtable, hashing three characters to an index for window, so |
691 |
/// that window[index]..window[index+2] have this hash code. |
692 |
/// Note that the array should really be unsigned short, so you need |
693 |
/// to and the values with 0xffff. |
694 |
/// </summary> |
695 |
readonly short[] head; |
696 |
|
697 |
/// <summary> |
698 |
/// <code>prev[index & WMASK]</code> points to the previous index that has the |
699 |
/// same hash code as the string starting at index. This way |
700 |
/// entries with the same hash code are in a linked list. |
701 |
/// Note that the array should really be unsigned short, so you need |
702 |
/// to and the values with 0xffff. |
703 |
/// </summary> |
704 |
readonly short[] prev; |
705 |
|
706 |
int matchStart; |
707 |
// Length of best match |
708 |
int matchLen; |
709 |
// Set if previous match exists |
710 |
bool prevAvailable; |
711 |
int blockStart; |
712 |
|
713 |
/// <summary> |
714 |
/// Points to the current character in the window. |
715 |
/// </summary> |
716 |
int strstart; |
717 |
|
718 |
/// <summary> |
719 |
/// lookahead is the number of characters starting at strstart in |
720 |
/// window that are valid. |
721 |
/// So window[strstart] until window[strstart+lookahead-1] are valid |
722 |
/// characters. |
723 |
/// </summary> |
724 |
int lookahead; |
725 |
|
726 |
/// <summary> |
727 |
/// This array contains the part of the uncompressed stream that |
728 |
/// is of relevance. The current character is indexed by strstart. |
729 |
/// </summary> |
730 |
readonly byte[] window; |
731 |
|
732 |
DeflateStrategy strategy; |
733 |
int max_chain, max_lazy, niceLength, goodLength; |
734 |
|
735 |
/// <summary> |
736 |
/// The current compression function. |
737 |
/// </summary> |
738 |
int compressionFunction; |
739 |
|
740 |
/// <summary> |
741 |
/// The input data for compression. |
742 |
/// </summary> |
743 |
byte[] inputBuf; |
744 |
|
745 |
/// <summary> |
746 |
/// The total bytes of input read. |
747 |
/// </summary> |
748 |
int totalIn; |
749 |
|
750 |
/// <summary> |
751 |
/// The offset into inputBuf, where input data starts. |
752 |
/// </summary> |
753 |
int inputOff; |
754 |
|
755 |
/// <summary> |
756 |
/// The end offset of the input data. |
757 |
/// </summary> |
758 |
int inputEnd; |
759 |
|
760 |
readonly DeflaterPending pending; |
761 |
readonly DeflaterHuffman huffman; |
762 |
|
763 |
/// <summary> |
764 |
/// The adler checksum |
765 |
/// </summary> |
766 |
readonly Adler32 adler; |
767 |
#endregion |
768 |
} |
769 |
} |