2 // Copyright (C) 2001 Mike Krueger
4 // This file was translated from java, it was part of the GNU Classpath
5 // Copyright (C) 2001 Free Software Foundation, Inc.
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 // Linking this library statically or dynamically with other modules is
22 // making a combined work based on this library. Thus, the terms and
23 // conditions of the GNU General Public License cover the whole
26 // As a special exception, the copyright holders of this library give you
27 // permission to link this library with independent modules to produce an
28 // executable, regardless of the license terms of these independent
29 // modules, and to copy and distribute the resulting executable under
30 // terms of your choice, provided that you also meet, for each linked
31 // independent module, the terms and conditions of the license of that
32 // module. An independent module is a module which is not derived from
33 // or based on this library. If you modify this library, you may extend
34 // this exception to your version of the library, but you are not
35 // obligated to do so. If you do not wish to do so, delete this
36 // exception statement from your version.
40 using ICSharpCode
.SharpZipLib
.Checksums
;
42 namespace ICSharpCode
.SharpZipLib
.Zip
.Compression
45 public enum DeflateStrategy
47 // The default strategy.
50 // This strategy will only allow longer string repetitions. It is
51 // useful for random data with a small character set.
54 // This strategy will not look for string repetitions at all. It
55 // only encodes with Huffman trees (which means, that more common
56 // characters get a smaller encoding.
60 public class DeflaterEngine
: DeflaterConstants
62 static int TOO_FAR
= 4096;
65 // private byte[] buffer;
69 int matchStart
, matchLen
;
72 int strstart
, lookahead
;
75 DeflateStrategy strategy
;
76 int max_chain
, max_lazy
, niceLength
, goodLength
;
79 /// The current compression function.
84 /// The input data for compression.
89 /// The total bytes of input read.
94 /// The offset into inputBuf, where input data starts.
99 /// The end offset of the input data.
103 DeflaterPending pending
;
104 DeflaterHuffman huffman
;
107 /// The adler checksum
111 public DeflaterEngine(DeflaterPending pending
)
113 this.pending
= pending
;
114 huffman
= new DeflaterHuffman(pending
);
115 adler
= new Adler32();
117 window
= new byte[2 * WSIZE
];
118 head
= new short[HASH_SIZE
];
119 prev
= new short[WSIZE
];
121 /* We start at index 1, to avoid a implementation deficiency, that
122 * we cannot build a repeat pattern at index 0.
124 blockStart
= strstart
= 1;
131 blockStart
= strstart
= 1;
134 prevAvailable
= false;
135 matchLen
= MIN_MATCH
- 1;
137 for (int i
= 0; i
< HASH_SIZE
; i
++) {
141 for (int i
= 0; i
< WSIZE
; i
++) {
146 public void ResetAdler()
153 return (int)adler
.Value
;
163 public DeflateStrategy Strategy
{
172 public void SetLevel(int lvl
)
174 goodLength
= DeflaterConstants
.GOOD_LENGTH
[lvl
];
175 max_lazy
= DeflaterConstants
.MAX_LAZY
[lvl
];
176 niceLength
= DeflaterConstants
.NICE_LENGTH
[lvl
];
177 max_chain
= DeflaterConstants
.MAX_CHAIN
[lvl
];
179 if (DeflaterConstants
.COMPR_FUNC
[lvl
] != comprFunc
) {
180 // if (DeflaterConstants.DEBUGGING) {
181 // //Console.WriteLine("Change from "+comprFunc +" to "
182 // + DeflaterConstants.COMPR_FUNC[lvl]);
186 if (strstart
> blockStart
) {
187 huffman
.FlushStoredBlock(window
, blockStart
,
188 strstart
- blockStart
, false);
189 blockStart
= strstart
;
194 if (strstart
> blockStart
) {
195 huffman
.FlushBlock(window
, blockStart
, strstart
- blockStart
,
197 blockStart
= strstart
;
202 huffman
.TallyLit(window
[strstart
-1] & 0xff);
204 if (strstart
> blockStart
) {
205 huffman
.FlushBlock(window
, blockStart
, strstart
- blockStart
, false);
206 blockStart
= strstart
;
208 prevAvailable
= false;
209 matchLen
= MIN_MATCH
- 1;
212 comprFunc
= COMPR_FUNC
[lvl
];
219 // //Console.WriteLine("updateHash: "+strstart);
221 ins_h
= (window
[strstart
] << HASH_SHIFT
) ^ window
[strstart
+ 1];
227 int hash
= ((ins_h
<< HASH_SHIFT
) ^ window
[strstart
+ (MIN_MATCH
-1)]) & HASH_MASK
;
230 // if (hash != (((window[strstart] << (2*HASH_SHIFT)) ^
231 // (window[strstart + 1] << HASH_SHIFT) ^
232 // (window[strstart + 2])) & HASH_MASK)) {
233 // throw new Exception("hash inconsistent: "+hash+"/"
234 // +window[strstart]+","
235 // +window[strstart+1]+","
236 // +window[strstart+2]+","+HASH_SHIFT);
240 prev
[strstart
& WMASK
] = match
= head
[hash
];
241 head
[hash
] = (short)strstart
;
243 return match
& 0xffff;
248 Array
.Copy(window
, WSIZE
, window
, 0, WSIZE
);
253 /* Slide the hash table (could be avoided with 32 bit values
254 * at the expense of memory usage).
256 for (int i
= 0; i
< HASH_SIZE
; ++i
) {
257 int m
= head
[i
] & 0xffff;
258 head
[i
] = (short)(m
>= WSIZE
? (m
- WSIZE
) : 0);
261 /* Slide the prev table. */
262 for (int i
= 0; i
< WSIZE
; i
++) {
263 int m
= prev
[i
] & 0xffff;
264 prev
[i
] = (short)(m
>= WSIZE
? (m
- WSIZE
) : 0);
268 public void FillWindow()
270 /* If the window is almost full and there is insufficient lookahead,
271 * move the upper half to the lower one to make room in the upper half.
273 if (strstart
>= WSIZE
+ MAX_DIST
) {
277 /* If there is not enough lookahead, but still some input left,
280 while (lookahead
< DeflaterConstants
.MIN_LOOKAHEAD
&& inputOff
< inputEnd
) {
281 int more
= 2 * WSIZE
- lookahead
- strstart
;
283 if (more
> inputEnd
- inputOff
) {
284 more
= inputEnd
- inputOff
;
287 System
.Array
.Copy(inputBuf
, inputOff
, window
, strstart
+ lookahead
, more
);
288 adler
.Update(inputBuf
, inputOff
, more
);
295 if (lookahead
>= MIN_MATCH
) {
300 bool FindLongestMatch(int curMatch
)
302 int chainLength
= this.max_chain
;
303 int niceLength
= this.niceLength
;
304 short[] prev
= this.prev
;
305 int scan
= this.strstart
;
307 int best_end
= this.strstart
+ matchLen
;
308 int best_len
= Math
.Max(matchLen
, MIN_MATCH
- 1);
310 int limit
= Math
.Max(strstart
- MAX_DIST
, 0);
312 int strend
= strstart
+ MAX_MATCH
- 1;
313 byte scan_end1
= window
[best_end
- 1];
314 byte scan_end
= window
[best_end
];
316 /* Do not waste too much time if we already have a good match: */
317 if (best_len
>= this.goodLength
) {
321 /* Do not look for matches beyond the end of the input. This is necessary
322 * to make deflate deterministic.
324 if (niceLength
> lookahead
) {
325 niceLength
= lookahead
;
328 if (DeflaterConstants
.DEBUGGING
&& strstart
> 2 * WSIZE
- MIN_LOOKAHEAD
) {
329 throw new InvalidOperationException("need lookahead");
333 if (DeflaterConstants
.DEBUGGING
&& curMatch
>= strstart
) {
334 throw new InvalidOperationException("future match");
336 if (window
[curMatch
+ best_len
] != scan_end
||
337 window
[curMatch
+ best_len
- 1] != scan_end1
||
338 window
[curMatch
] != window
[scan
] ||
339 window
[curMatch
+ 1] != window
[scan
+ 1]) {
343 match
= curMatch
+ 2;
346 /* We check for insufficient lookahead only every 8th comparison;
347 * the 256th check will be made at strstart+258.
349 while (window
[++scan
] == window
[++match
] &&
350 window
[++scan
] == window
[++match
] &&
351 window
[++scan
] == window
[++match
] &&
352 window
[++scan
] == window
[++match
] &&
353 window
[++scan
] == window
[++match
] &&
354 window
[++scan
] == window
[++match
] &&
355 window
[++scan
] == window
[++match
] &&
356 window
[++scan
] == window
[++match
] && scan
< strend
) ;
358 if (scan
> best_end
) {
359 // if (DeflaterConstants.DEBUGGING && ins_h == 0)
360 // System.err.println("Found match: "+curMatch+"-"+(scan-strstart));
361 matchStart
= curMatch
;
363 best_len
= scan
- strstart
;
365 if (best_len
>= niceLength
) {
369 scan_end1
= window
[best_end
- 1];
370 scan_end
= window
[best_end
];
373 } while ((curMatch
= (prev
[curMatch
& WMASK
] & 0xffff)) > limit
&& --chainLength
!= 0);
375 matchLen
= Math
.Min(best_len
, lookahead
);
376 return matchLen
>= MIN_MATCH
;
379 public void SetDictionary(byte[] buffer
, int offset
, int length
)
381 if (DeflaterConstants
.DEBUGGING
&& strstart
!= 1) {
382 throw new InvalidOperationException("strstart not 1");
384 adler
.Update(buffer
, offset
, length
);
385 if (length
< MIN_MATCH
) {
388 if (length
> MAX_DIST
) {
389 offset
+= length
- MAX_DIST
;
393 System
.Array
.Copy(buffer
, offset
, window
, strstart
, length
);
397 while (--length
> 0) {
402 blockStart
= strstart
;
405 bool DeflateStored(bool flush
, bool finish
)
407 if (!flush
&& lookahead
== 0) {
411 strstart
+= lookahead
;
414 int storedLen
= strstart
- blockStart
;
416 if ((storedLen
>= DeflaterConstants
.MAX_BLOCK_SIZE
) || /* Block is full */
417 (blockStart
< WSIZE
&& storedLen
>= MAX_DIST
) || /* Block may move out of window */
419 bool lastBlock
= finish
;
420 if (storedLen
> DeflaterConstants
.MAX_BLOCK_SIZE
) {
421 storedLen
= DeflaterConstants
.MAX_BLOCK_SIZE
;
425 // if (DeflaterConstants.DEBUGGING) {
426 // //Console.WriteLine("storedBlock["+storedLen+","+lastBlock+"]");
429 huffman
.FlushStoredBlock(window
, blockStart
, storedLen
, lastBlock
);
430 blockStart
+= storedLen
;
436 private bool DeflateFast(bool flush
, bool finish
)
438 if (lookahead
< MIN_LOOKAHEAD
&& !flush
) {
442 while (lookahead
>= MIN_LOOKAHEAD
|| flush
) {
443 if (lookahead
== 0) {
444 /* We are flushing everything */
445 huffman
.FlushBlock(window
, blockStart
, strstart
- blockStart
, finish
);
446 blockStart
= strstart
;
450 if (strstart
> 2 * WSIZE
- MIN_LOOKAHEAD
) {
451 /* slide window, as findLongestMatch need this.
452 * This should only happen when flushing and the window
459 if (lookahead
>= MIN_MATCH
&&
460 (hashHead
= InsertString()) != 0 &&
461 strategy
!= DeflateStrategy
.HuffmanOnly
&&
462 strstart
- hashHead
<= MAX_DIST
&&
463 FindLongestMatch(hashHead
)) {
464 /* longestMatch sets matchStart and matchLen */
465 // if (DeflaterConstants.DEBUGGING) {
466 // for (int i = 0 ; i < matchLen; i++) {
467 // if (window[strstart+i] != window[matchStart + i]) {
468 // throw new Exception();
473 // -jr- Hak hak hak this stops problems with fast/low compression and index out of range
474 if (huffman
.TallyDist(strstart
- matchStart
, matchLen
)) {
475 bool lastBlock
= finish
&& lookahead
== 0;
476 huffman
.FlushBlock(window
, blockStart
, strstart
- blockStart
, lastBlock
);
477 blockStart
= strstart
;
480 lookahead
-= matchLen
;
481 if (matchLen
<= max_lazy
&& lookahead
>= MIN_MATCH
) {
482 while (--matchLen
> 0) {
488 strstart
+= matchLen
;
489 if (lookahead
>= MIN_MATCH
- 1) {
493 matchLen
= MIN_MATCH
- 1;
497 huffman
.TallyLit(window
[strstart
] & 0xff);
502 if (huffman
.IsFull()) {
503 bool lastBlock
= finish
&& lookahead
== 0;
504 huffman
.FlushBlock(window
, blockStart
, strstart
- blockStart
, lastBlock
);
505 blockStart
= strstart
;
512 bool DeflateSlow(bool flush
, bool finish
)
514 if (lookahead
< MIN_LOOKAHEAD
&& !flush
) {
518 while (lookahead
>= MIN_LOOKAHEAD
|| flush
) {
519 if (lookahead
== 0) {
521 huffman
.TallyLit(window
[strstart
-1] & 0xff);
523 prevAvailable
= false;
525 /* We are flushing everything */
526 if (DeflaterConstants
.DEBUGGING
&& !flush
) {
527 throw new Exception("Not flushing, but no lookahead");
529 huffman
.FlushBlock(window
, blockStart
, strstart
- blockStart
,
531 blockStart
= strstart
;
535 if (strstart
>= 2 * WSIZE
- MIN_LOOKAHEAD
) {
536 /* slide window, as findLongestMatch need this.
537 * This should only happen when flushing and the window
543 int prevMatch
= matchStart
;
544 int prevLen
= matchLen
;
545 if (lookahead
>= MIN_MATCH
) {
546 int hashHead
= InsertString();
547 if (strategy
!= DeflateStrategy
.HuffmanOnly
&& hashHead
!= 0 && strstart
- hashHead
<= MAX_DIST
&& FindLongestMatch(hashHead
)) {
548 /* longestMatch sets matchStart and matchLen */
550 /* Discard match if too small and too far away */
551 if (matchLen
<= 5 && (strategy
== DeflateStrategy
.Filtered
|| (matchLen
== MIN_MATCH
&& strstart
- matchStart
> TOO_FAR
))) {
552 matchLen
= MIN_MATCH
- 1;
557 /* previous match was better */
558 if (prevLen
>= MIN_MATCH
&& matchLen
<= prevLen
) {
559 // if (DeflaterConstants.DEBUGGING) {
560 // for (int i = 0 ; i < matchLen; i++) {
561 // if (window[strstart-1+i] != window[prevMatch + i])
562 // throw new Exception();
565 huffman
.TallyDist(strstart
- 1 - prevMatch
, prevLen
);
570 if (lookahead
>= MIN_MATCH
) {
573 } while (--prevLen
> 0);
576 prevAvailable
= false;
577 matchLen
= MIN_MATCH
- 1;
580 huffman
.TallyLit(window
[strstart
-1] & 0xff);
582 prevAvailable
= true;
587 if (huffman
.IsFull()) {
588 int len
= strstart
- blockStart
;
592 bool lastBlock
= (finish
&& lookahead
== 0 && !prevAvailable
);
593 huffman
.FlushBlock(window
, blockStart
, len
, lastBlock
);
601 public bool Deflate(bool flush
, bool finish
)
606 bool canFlush
= flush
&& inputOff
== inputEnd
;
607 // if (DeflaterConstants.DEBUGGING) {
608 // //Console.WriteLine("window: ["+blockStart+","+strstart+","
609 // +lookahead+"], "+comprFunc+","+canFlush);
613 progress
= DeflateStored(canFlush
, finish
);
616 progress
= DeflateFast(canFlush
, finish
);
619 progress
= DeflateSlow(canFlush
, finish
);
622 throw new InvalidOperationException("unknown comprFunc");
624 } while (pending
.IsFlushed
&& progress
); /* repeat while we have no pending output and progress was made */
628 public void SetInput(byte[] buf
, int off
, int len
)
630 if (inputOff
< inputEnd
) {
631 throw new InvalidOperationException("Old input was not completely processed");
636 /* We want to throw an ArrayIndexOutOfBoundsException early. The
637 * check is very tricky: it also handles integer wrap around.
639 if (0 > off
|| off
> end
|| end
> buf
.Length
) {
640 throw new ArgumentOutOfRangeException();
648 public bool NeedsInput()
650 return inputEnd
== inputOff
;