1
/* DeflaterEngine.java --
2
Copyright (C) 2001, 2004, 2005 Free Software Foundation, Inc.
4
This file is part of GNU Classpath.
6
GNU Classpath is free software; you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 2, or (at your option)
11
GNU Classpath is distributed in the hope that it will be useful, but
12
WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
General Public License for more details.
16
You should have received a copy of the GNU General Public License
17
along with GNU Classpath; see the file COPYING. If not, write to the
18
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
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. */
38
package java.util.zip;
40
final class DeflaterEngine implements DeflaterConstants
42
private static final int TOO_FAR = 4096;
47
* Hashtable, hashing three characters to an index for window, so
48
* that window[index]..window[index+2] have this hash code.
49
* Note that the array should really be unsigned short, so you need
50
* to and the values with 0xffff.
55
* prev[index & WMASK] points to the previous index that has the
56
* same hash code as the string starting at index. This way
57
* entries with the same hash code are in a linked list.
58
* Note that the array should really be unsigned short, so you need
59
* to and the values with 0xffff.
63
private int matchStart, matchLen;
64
private boolean prevAvailable;
65
private int blockStart;
68
* strstart points to the current character in window.
73
* lookahead is the number of characters starting at strstart in
74
* window that are valid.
75
* So window[strstart] until window[strstart+lookahead-1] are valid
78
private int lookahead;
81
* This array contains the part of the uncompressed stream that
82
* is of relevance. The current character is indexed by strstart.
84
private byte[] window;
86
private int strategy, max_chain, max_lazy, niceLength, goodLength;
88
/** The current compression function. */
89
private int comprFunc;
91
/** The input data for compression. */
92
private byte[] inputBuf;
94
/** The total bytes of input read. */
97
/** The offset into inputBuf, where input data starts. */
100
/** The end offset of the input data. */
101
private int inputEnd;
103
private DeflaterPending pending;
104
private DeflaterHuffman huffman;
106
/** The adler checksum */
107
private Adler32 adler;
109
/* DEFLATE ALGORITHM:
111
* The uncompressed stream is inserted into the window array. When
112
* the window array is full the first half is thrown away and the
113
* second half is copied to the beginning.
115
* The head array is a hash table. Three characters build a hash value
116
* and they the value points to the corresponding index in window of
117
* the last string with this hash. The prev array implements a
118
* linked list of matches with the same hash: prev[index & WMASK] points
119
* to the previous index with the same hash.
125
DeflaterEngine(DeflaterPending pending) {
126
this.pending = pending;
127
huffman = new DeflaterHuffman(pending);
128
adler = new Adler32();
130
window = new byte[2*WSIZE];
131
head = new short[HASH_SIZE];
132
prev = new short[WSIZE];
134
/* We start at index 1, to avoid a implementation deficiency, that
135
* we cannot build a repeat pattern at index 0.
137
blockStart = strstart = 1;
148
final void clearHash()
150
blockStart = strstart = 1;
152
prevAvailable = false;
153
matchLen = MIN_MATCH - 1;
154
for (int i = 0; i < HASH_SIZE; i++)
156
for (int i = 0; i < WSIZE; i++)
160
public final void resetAdler()
165
public final int getAdler()
167
int chksum = (int) adler.getValue();
171
public final long getTotalIn()
176
public final void setStrategy(int strat)
181
public void setLevel(int lvl)
183
goodLength = DeflaterConstants.GOOD_LENGTH[lvl];
184
max_lazy = DeflaterConstants.MAX_LAZY[lvl];
185
niceLength = DeflaterConstants.NICE_LENGTH[lvl];
186
max_chain = DeflaterConstants.MAX_CHAIN[lvl];
188
if (DeflaterConstants.COMPR_FUNC[lvl] != comprFunc)
190
if (DeflaterConstants.DEBUGGING)
191
System.err.println("Change from "+comprFunc +" to "
192
+ DeflaterConstants.COMPR_FUNC[lvl]);
196
if (strstart > blockStart)
198
huffman.flushStoredBlock(window, blockStart,
199
strstart - blockStart, false);
200
blockStart = strstart;
205
if (strstart > blockStart)
207
huffman.flushBlock(window, blockStart, strstart - blockStart,
209
blockStart = strstart;
214
huffman.tallyLit(window[strstart-1] & 0xff);
215
if (strstart > blockStart)
217
huffman.flushBlock(window, blockStart, strstart - blockStart,
219
blockStart = strstart;
221
prevAvailable = false;
222
matchLen = MIN_MATCH - 1;
225
comprFunc = COMPR_FUNC[lvl];
229
private void updateHash() {
231
System.err.println("updateHash: "+strstart);
232
ins_h = (window[strstart] << HASH_SHIFT) ^ window[strstart + 1];
236
* Inserts the current string in the head hash and returns the previous
237
* value for this hash.
239
private int insertString() {
241
int hash = ((ins_h << HASH_SHIFT) ^ window[strstart + (MIN_MATCH -1)])
246
if (hash != (((window[strstart] << (2*HASH_SHIFT))
247
^ (window[strstart + 1] << HASH_SHIFT)
248
^ (window[strstart + 2])) & HASH_MASK))
249
throw new InternalError("hash inconsistent: "+hash+"/"
250
+window[strstart]+","
251
+window[strstart+1]+","
252
+window[strstart+2]+","+HASH_SHIFT);
255
prev[strstart & WMASK] = match = head[hash];
256
head[hash] = (short) strstart;
258
return match & 0xffff;
261
private void slideWindow()
263
System.arraycopy(window, WSIZE, window, 0, WSIZE);
268
/* Slide the hash table (could be avoided with 32 bit values
269
* at the expense of memory usage).
271
for (int i = 0; i < HASH_SIZE; i++)
273
int m = head[i] & 0xffff;
274
head[i] = m >= WSIZE ? (short) (m - WSIZE) : 0;
277
/* Slide the prev table.
279
for (int i = 0; i < WSIZE; i++)
281
int m = prev[i] & 0xffff;
282
prev[i] = m >= WSIZE ? (short) (m - WSIZE) : 0;
287
* Fill the window when the lookahead becomes insufficient.
288
* Updates strstart and lookahead.
290
* OUT assertions: strstart + lookahead <= 2*WSIZE
291
* lookahead >= MIN_LOOKAHEAD or inputOff == inputEnd
293
private void fillWindow()
295
/* If the window is almost full and there is insufficient lookahead,
296
* move the upper half to the lower one to make room in the upper half.
298
if (strstart >= WSIZE + MAX_DIST)
301
/* If there is not enough lookahead, but still some input left,
304
while (lookahead < DeflaterConstants.MIN_LOOKAHEAD && inputOff < inputEnd)
306
int more = 2*WSIZE - lookahead - strstart;
308
if (more > inputEnd - inputOff)
309
more = inputEnd - inputOff;
311
System.arraycopy(inputBuf, inputOff,
312
window, strstart + lookahead, more);
313
adler.update(inputBuf, inputOff, more);
319
if (lookahead >= MIN_MATCH)
324
* Find the best (longest) string in the window matching the
325
* string starting at strstart.
328
* strstart + MAX_MATCH <= window.length.
333
private boolean findLongestMatch(int curMatch) {
334
int chainLength = this.max_chain;
335
int niceLength = this.niceLength;
336
short[] prev = this.prev;
337
int scan = this.strstart;
339
int best_end = this.strstart + matchLen;
340
int best_len = Math.max(matchLen, MIN_MATCH - 1);
342
int limit = Math.max(strstart - MAX_DIST, 0);
344
int strend = scan + MAX_MATCH - 1;
345
byte scan_end1 = window[best_end - 1];
346
byte scan_end = window[best_end];
348
/* Do not waste too much time if we already have a good match: */
349
if (best_len >= this.goodLength)
352
/* Do not look for matches beyond the end of the input. This is necessary
353
* to make deflate deterministic.
355
if (niceLength > lookahead)
356
niceLength = lookahead;
358
if (DeflaterConstants.DEBUGGING
359
&& strstart > 2*WSIZE - MIN_LOOKAHEAD)
360
throw new InternalError("need lookahead");
363
if (DeflaterConstants.DEBUGGING && curMatch >= strstart)
364
throw new InternalError("future match");
365
if (window[curMatch + best_len] != scan_end
366
|| window[curMatch + best_len - 1] != scan_end1
367
|| window[curMatch] != window[scan]
368
|| window[curMatch+1] != window[scan + 1])
371
match = curMatch + 2;
374
/* We check for insufficient lookahead only every 8th comparison;
375
* the 256th check will be made at strstart+258.
377
while (window[++scan] == window[++match]
378
&& window[++scan] == window[++match]
379
&& window[++scan] == window[++match]
380
&& window[++scan] == window[++match]
381
&& window[++scan] == window[++match]
382
&& window[++scan] == window[++match]
383
&& window[++scan] == window[++match]
384
&& window[++scan] == window[++match]
388
if (scan > best_end) {
389
// if (DeflaterConstants.DEBUGGING && ins_h == 0)
390
// System.err.println("Found match: "+curMatch+"-"+(scan-strstart));
391
matchStart = curMatch;
393
best_len = scan - strstart;
394
if (best_len >= niceLength)
397
scan_end1 = window[best_end-1];
398
scan_end = window[best_end];
401
} while ((curMatch = (prev[curMatch & WMASK] & 0xffff)) > limit
402
&& --chainLength != 0);
404
matchLen = Math.min(best_len, lookahead);
405
return matchLen >= MIN_MATCH;
408
void setDictionary(byte[] buffer, int offset, int length) {
409
if (DeflaterConstants.DEBUGGING && strstart != 1)
410
throw new IllegalStateException("strstart not 1");
411
adler.update(buffer, offset, length);
412
if (length < MIN_MATCH)
414
if (length > MAX_DIST) {
415
offset += length - MAX_DIST;
419
System.arraycopy(buffer, offset, window, strstart, length);
429
blockStart = strstart;
432
private boolean deflateStored(boolean flush, boolean finish)
434
if (!flush && lookahead == 0)
437
strstart += lookahead;
440
int storedLen = strstart - blockStart;
442
if ((storedLen >= DeflaterConstants.MAX_BLOCK_SIZE)
444
|| (blockStart < WSIZE && storedLen >= MAX_DIST)
445
/* Block may move out of window */
448
boolean lastBlock = finish;
449
if (storedLen > DeflaterConstants.MAX_BLOCK_SIZE)
451
storedLen = DeflaterConstants.MAX_BLOCK_SIZE;
455
if (DeflaterConstants.DEBUGGING)
456
System.err.println("storedBlock["+storedLen+","+lastBlock+"]");
458
huffman.flushStoredBlock(window, blockStart, storedLen, lastBlock);
459
blockStart += storedLen;
465
private boolean deflateFast(boolean flush, boolean finish)
467
if (lookahead < MIN_LOOKAHEAD && !flush)
470
while (lookahead >= MIN_LOOKAHEAD || flush)
474
/* We are flushing everything */
475
huffman.flushBlock(window, blockStart, strstart - blockStart,
477
blockStart = strstart;
481
if (strstart > 2 * WSIZE - MIN_LOOKAHEAD)
483
/* slide window, as findLongestMatch need this.
484
* This should only happen when flushing and the window
491
if (lookahead >= MIN_MATCH
492
&& (hashHead = insertString()) != 0
493
&& strategy != Deflater.HUFFMAN_ONLY
494
&& strstart - hashHead <= MAX_DIST
495
&& findLongestMatch(hashHead))
497
/* longestMatch sets matchStart and matchLen */
498
if (DeflaterConstants.DEBUGGING)
500
for (int i = 0 ; i < matchLen; i++)
502
if (window[strstart+i] != window[matchStart + i])
503
throw new InternalError();
506
boolean full = huffman.tallyDist(strstart - matchStart, matchLen);
508
lookahead -= matchLen;
509
if (matchLen <= max_lazy && lookahead >= MIN_MATCH)
511
while (--matchLen > 0)
520
strstart += matchLen;
521
if (lookahead >= MIN_MATCH - 1)
524
matchLen = MIN_MATCH - 1;
531
huffman.tallyLit(window[strstart] & 0xff);
536
if (huffman.isFull())
538
boolean lastBlock = finish && lookahead == 0;
539
huffman.flushBlock(window, blockStart, strstart - blockStart,
541
blockStart = strstart;
548
private boolean deflateSlow(boolean flush, boolean finish)
550
if (lookahead < MIN_LOOKAHEAD && !flush)
553
while (lookahead >= MIN_LOOKAHEAD || flush)
558
huffman.tallyLit(window[strstart-1] & 0xff);
559
prevAvailable = false;
561
/* We are flushing everything */
562
if (DeflaterConstants.DEBUGGING && !flush)
563
throw new InternalError("Not flushing, but no lookahead");
564
huffman.flushBlock(window, blockStart, strstart - blockStart,
566
blockStart = strstart;
570
if (strstart >= 2 * WSIZE - MIN_LOOKAHEAD)
572
/* slide window, as findLongestMatch need this.
573
* This should only happen when flushing and the window
579
int prevMatch = matchStart;
580
int prevLen = matchLen;
581
if (lookahead >= MIN_MATCH)
583
int hashHead = insertString();
584
if (strategy != Deflater.HUFFMAN_ONLY
585
&& hashHead != 0 && strstart - hashHead <= MAX_DIST
586
&& findLongestMatch(hashHead))
588
/* longestMatch sets matchStart and matchLen */
590
/* Discard match if too small and too far away */
592
&& (strategy == Deflater.FILTERED
593
|| (matchLen == MIN_MATCH
594
&& strstart - matchStart > TOO_FAR))) {
595
matchLen = MIN_MATCH - 1;
600
/* previous match was better */
601
if (prevLen >= MIN_MATCH && matchLen <= prevLen)
603
if (DeflaterConstants.DEBUGGING)
605
for (int i = 0 ; i < matchLen; i++)
607
if (window[strstart-1+i] != window[prevMatch + i])
608
throw new InternalError();
611
huffman.tallyDist(strstart - 1 - prevMatch, prevLen);
617
if (lookahead >= MIN_MATCH)
620
while (--prevLen > 0);
623
prevAvailable = false;
624
matchLen = MIN_MATCH - 1;
629
huffman.tallyLit(window[strstart-1] & 0xff);
630
prevAvailable = true;
635
if (huffman.isFull())
637
int len = strstart - blockStart;
640
boolean lastBlock = (finish && lookahead == 0 && !prevAvailable);
641
huffman.flushBlock(window, blockStart, len, lastBlock);
649
public boolean deflate(boolean flush, boolean finish)
655
boolean canFlush = flush && inputOff == inputEnd;
656
if (DeflaterConstants.DEBUGGING)
657
System.err.println("window: ["+blockStart+","+strstart+","
658
+lookahead+"], "+comprFunc+","+canFlush);
662
progress = deflateStored(canFlush, finish);
665
progress = deflateFast(canFlush, finish);
668
progress = deflateSlow(canFlush, finish);
671
throw new InternalError();
674
while (pending.isFlushed() /* repeat while we have no pending output */
675
&& progress); /* and progress was made */
680
void setInput(byte[] buf, int off, int len)
682
// caller has already checked parameters
685
inputEnd = off + len;
688
public final boolean needsInput()
690
return inputEnd == inputOff;