/* | |
* DEFLATE library (Java) | |
* Copyright (c) Project Nayuki | |
* | |
* https://www.nayuki.io/page/deflate-library-java | |
* https://github.com/nayuki/DEFLATE-library-Java | |
* Modified for Eclipse Memory Analyzer for: | |
* cloning of inflator | |
* pass-through mode for multi-chunk zips | |
* mark/reset | |
* merge of output buffer and dictionary | |
*/ | |
package io.nayuki.deflate; | |
import java.io.EOFException; | |
import java.io.FilterInputStream; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.util.Arrays; | |
import java.util.Objects; | |
/** | |
* Decompresses a DEFLATE data stream (raw format without zlib or gzip headers or footers) into a byte stream. | |
*/ | |
public final class InflaterInputStream extends FilterInputStream { | |
/*---- Fields ----*/ | |
/* Data buffers */ | |
// Buffer of bytes read from in.read() (the underlying input stream) | |
private byte[] inputBuffer; // Can have any positive length (but longer means less overhead) | |
private int inputBufferLength; // Number of valid prefix bytes, at least 0 | |
private int inputBufferIndex; // Index of next byte to consume | |
// Buffer of bits packed from the bytes in 'inputBuffer' | |
private long inputBitBuffer; // 0 <= value < 2^inputBitBufferLength | |
private int inputBitBufferLength; // Always in the range [0, 63] | |
// Queued bytes to yield first when this.read() is called | |
private int outputBufferLength; // Number of valid prefix bytes, at least 0 | |
private int outputBufferIndex; // Index of next byte to produce, in the range [0, outputBufferLength] | |
// Buffer of last 32 KiB of decoded data, for LZ77 decompression | |
private byte[] dictionary; | |
private int dictionaryIndex; | |
// Generally speaking, the overall data flow of this decompressor looks like this: | |
// in (the underlying input stream, declared in the superclass) -> in.read() | |
// -> inputBuffer -> packing logic in readBits() | |
// -> inputBitBuffer -> readBit() or equivalent | |
// -> various literal and length-distance symbols -> LZ77 decoding logic | |
// -> dictionary, sometimes also outputBuffer -> copying to the caller's array | |
// -> b (the array passed into this.read(byte[],int,int)). | |
/* Configuration */ | |
// Indicates whether mark() should be called when the underlying | |
// input stream is read, and whether calling detach() is allowed. | |
private final boolean isDetachable; | |
/* State */ | |
// The state of the decompressor: | |
// -4: The decompressor is in pass-through mode, indefinite uncompressed | |
// -3: This decompressor stream has been closed. Equivalent to in == null. | |
// -2: A data format exception has been thrown. Equivalent to exception != null. | |
// -1: Currently processing a Huffman-compressed block. | |
// 0: Initial state, or a block just ended. | |
// 1 to 65535: Currently processing an uncompressed block, number of bytes remaining. | |
private int state; | |
// A saved exception that is thrown on every read() or detach(). | |
private IOException exception; | |
// Indicates whether a block header with the "bfinal" flag has been seen. | |
// This starts as false, should eventually become true, and never changes back to false. | |
// except for the Eclipse MAT changes when a new segment is attached | |
private boolean isLastBlock; | |
// Current code trees for when state == -1. When state != -1, both must be null. | |
private short[] literalLengthCodeTree; // When state == -1, this must be not null | |
private short[] literalLengthCodeTable; // Derived from literalLengthCodeTree; same nullness | |
private short[] distanceCodeTree; // When state == -1, this can be null or not null | |
private short[] distanceCodeTable; // Derived from distanceCodeTree; same nullness | |
/* | |
* For marking. | |
* markPos >= 0 is position of mark in buffer [dictionary] | |
* markPos = -1 not set | |
* markPos = -2 has been set, but nothing has been read | |
* markPos = -3 has been set but too much has been read | |
*/ | |
private int markPos = -1; | |
/*---- Constructors ----*/ | |
/** | |
* Constructs an inflater input stream over the specified underlying input stream, and with the | |
* specified option for detachability. The underlying stream must contain DEFLATE-compressed data with | |
* no headers or footers (e.g. must be unwrapped from the zlib or gzip container formats). Detachability | |
* allows {@link #detach()} to be called, and requires the specified input stream to support marking. | |
* @param in the underlying input stream of raw DEFLATE-compressed data | |
* @param detachable whether {@code detach()} can be called later | |
* @throws NullPointerException if the input stream is {@code null} | |
* @throws IllegalArgumentException if {@code detach == true} but {@code in.markSupported() == false} | |
*/ | |
public InflaterInputStream(InputStream in, boolean detachable) { | |
this(in, detachable, 16 * 1024); // Use a reasonable default input buffer size | |
} | |
/** | |
* Constructs an inflater input stream over the specified underlying input stream, with the | |
* specified options for detachability and input buffer size. The underlying stream must | |
* contain DEFLATE-compressed data with no headers or footers (e.g. must be unwrapped from | |
* the zlib or gzip container formats). Detachability allows {@link #detach()} to be called, | |
* and requires the specified input stream to support marking. | |
* @param in the underlying input stream of raw DEFLATE-compressed data | |
* @param detachable whether {@code detach()} can be called later | |
* @param inBufLen the size of the internal read buffer, which must be positive | |
* @throws NullPointerException if the input stream is {@code null} | |
* @throws IllegalArgumentException if {@code inBufLen < 1} | |
* @throws IllegalArgumentException if {@code detach == true} but {@code in.markSupported() == false} | |
*/ | |
public InflaterInputStream(InputStream in, boolean detachable, int inBufLen) { | |
// Handle the input stream and detachability | |
super(in); | |
if (inBufLen <= 0) | |
throw new IllegalArgumentException("Input buffer size must be positive"); | |
isDetachable = detachable; | |
if (detachable) { | |
if (in.markSupported()) | |
in.mark(0); | |
else | |
throw new IllegalArgumentException("Input stream not markable, cannot support detachment"); | |
} | |
// Initialize data buffers | |
inputBuffer = new byte[inBufLen]; | |
inputBufferLength = 0; | |
inputBufferIndex = 0; | |
inputBitBuffer = 0; | |
inputBitBufferLength = 0; | |
outputBufferLength = 0; | |
outputBufferIndex = 0; | |
dictionary = new byte[DICTIONARY_LENGTH]; | |
dictionaryIndex = 0; | |
// Initialize state | |
state = 0; | |
exception = null; | |
isLastBlock = false; | |
literalLengthCodeTree = null; | |
literalLengthCodeTable = null; | |
distanceCodeTree = null; | |
distanceCodeTable = null; | |
} | |
/** | |
* Extra constructor added for org.eclipse.mat.hprof | |
* | |
* Only safe to use copy or original once the underlying stream | |
* has been positioned to the appropriate location. | |
* Added by Eclipse MAT. | |
* @param copy the original stream | |
*/ | |
public InflaterInputStream(InflaterInputStream copy) | |
{ | |
this(copy.in, copy.isDetachable); | |
if (copy.inputBuffer != null) | |
inputBuffer = Arrays.copyOf(copy.inputBuffer, copy.inputBuffer.length); | |
inputBufferLength = copy.inputBufferLength; | |
inputBufferIndex = copy.inputBufferIndex; | |
inputBitBuffer = copy.inputBitBuffer; | |
inputBitBufferLength = copy.inputBitBufferLength; | |
outputBufferLength = copy.outputBufferLength; | |
outputBufferIndex = copy.outputBufferIndex; | |
if (copy.dictionary != null) | |
dictionary = Arrays.copyOf(copy.dictionary, copy.dictionary.length); | |
else | |
dictionary = null; | |
dictionaryIndex = copy.dictionaryIndex; | |
markPos = copy.markPos; | |
// Initialize state | |
state = copy.state; | |
if (copy.exception != null) | |
exception = new IOException(copy.exception.toString()); | |
isLastBlock = copy.isLastBlock; | |
if (copy.literalLengthCodeTree != null) | |
literalLengthCodeTree = Arrays.copyOf(copy.literalLengthCodeTree, copy.literalLengthCodeTree.length); | |
if (copy.literalLengthCodeTable != null) | |
literalLengthCodeTable = Arrays.copyOf(copy.literalLengthCodeTable, copy.literalLengthCodeTable.length); | |
if (copy.distanceCodeTree != null) | |
distanceCodeTree = Arrays.copyOf(copy.distanceCodeTree, copy.distanceCodeTree.length); | |
if (copy.distanceCodeTable != null) | |
distanceCodeTable = Arrays.copyOf(copy.distanceCodeTable, copy.distanceCodeTable.length); | |
} | |
/*---- Public API methods ----*/ | |
/** | |
* Reads the next byte of decompressed data from this stream. If data is available | |
* then a number in the range [0, 255] is returned (blocking if necessary); | |
* otherwise −1 is returned if the end of stream is reached. | |
* @return the next unsigned byte of data, or −1 for the end of stream | |
* @throws IOException if an I/O exception occurred in the underlying input stream, the end of | |
* stream was encountered at an unexpected position, or the compressed data has a format error | |
* @throws IllegalStateException if the stream has already been closed | |
*/ | |
public int read() throws IOException { | |
// In theory this method for reading a single byte could be implemented somewhat faster. | |
// We could take the logic of read(byte[],int,int) and simplify it for the special case | |
// of handling one byte. But if the caller chose to use this read() method instead of | |
// the bulk read(byte[]) method, then they have already chosen to not care about speed. | |
// Therefore speeding up this method would result in needless complexity. Instead, | |
// we chose to optimize this method for simplicity and ease of verifying correctness. | |
while (true) { | |
byte[] b = new byte[1]; | |
switch (read(b)) { | |
case 1: | |
return (b[0] & 0xFF); | |
case 0: | |
continue; | |
case -1: | |
return -1; | |
default: | |
throw new AssertionError(); | |
} | |
} | |
} | |
/** | |
* Reads some bytes from the decompressed data of this stream into the specified array's subrange. | |
* This returns the number of data bytes that were stored into the array, and is in the range | |
* [−1, len]. Note that 0 can be returned even if the end of stream hasn't been reached yet. | |
* @throws NullPointerException if the array is {@code null} | |
* @throws ArrayIndexOutOfBoundsException if the array subrange is out of bounds | |
* @throws IOException if an I/O exception occurred in the underlying input stream, the end of | |
* stream was encountered at an unexpected position, or the compressed data has a format error | |
* @throws IllegalStateException if the stream has already been closed | |
*/ | |
public int read(byte[] b, int off, int len) throws IOException { | |
// Check arguments and state | |
Objects.requireNonNull(b); | |
if (off < 0 || off > b.length || len < 0 || b.length - off < len) | |
throw new ArrayIndexOutOfBoundsException(); | |
if (in == null) | |
throw new IllegalStateException("Stream already closed"); | |
if (exception != null) | |
throw exception; | |
// Special handling for empty read request | |
if (len == 0) | |
return (outputBufferLength > 0 || state != 0 || !isLastBlock) ? 0 : -1; | |
assert len > 0; | |
int result = 0; // Number of bytes filled in the array 'b' | |
// First move bytes (if any) from the output buffer [which is actually in the dictionary] | |
if (outputBufferLength > 0) { | |
// There could be a new mark while we are re-reading data, or reading duplicated bytes. | |
if (markPos == -2) | |
markPos = (dictionaryIndex - (outputBufferLength - outputBufferIndex)) & DICTIONARY_MASK; | |
int n = Math.min(outputBufferLength - outputBufferIndex, len); | |
// Move up to end of dictionary array | |
int i = (dictionaryIndex - (outputBufferLength - outputBufferIndex)) & DICTIONARY_MASK; | |
int n1 = Math.min(n, DICTIONARY_LENGTH - i); | |
System.arraycopy(dictionary, i, b, off + result, n1); | |
result = n1; | |
outputBufferIndex += n1; | |
// Move the remainder | |
if (n1 < n) { | |
// Wrap | |
n1 = n - n1; | |
System.arraycopy(dictionary, 0, b, off + result, n1); | |
result += n1; | |
outputBufferIndex += n1; | |
} | |
if (outputBufferIndex == outputBufferLength) { | |
outputBufferLength = 0; | |
outputBufferIndex = 0; | |
} | |
if (result == len) | |
return result; | |
} | |
// Now the output buffer is clear, and we have room to read at least one byte | |
assert outputBufferLength == 0 && outputBufferIndex == 0 && result < len; | |
// Get into a block if not already inside one | |
while (state == 0) { | |
if (isLastBlock) | |
return (result != 0) ? result : -1; | |
// Read and process the block header | |
isLastBlock = readBits(1) == 1; | |
switch (readBits(2)) { // Type | |
case 0: | |
alignInputToByte(); | |
state = readBits(16); // Block length | |
if (state != (readBits(16) ^ 0xFFFF)) | |
destroyAndThrow(new DataFormatException("len/nlen mismatch in uncompressed block")); | |
break; | |
case 1: | |
state = -1; | |
literalLengthCodeTree = FIXED_LITERAL_LENGTH_CODE_TREE; | |
literalLengthCodeTable = FIXED_LITERAL_LENGTH_CODE_TABLE; | |
distanceCodeTree = FIXED_DISTANCE_CODE_TREE; | |
distanceCodeTable = FIXED_DISTANCE_CODE_TABLE; | |
break; | |
case 2: | |
state = -1; | |
decodeHuffmanCodes(); | |
break; | |
case 3: | |
destroyAndThrow(new DataFormatException("Reserved block type")); | |
break; | |
default: | |
throw new AssertionError(); | |
} | |
} | |
// Read the current block's data into the argument array | |
if (1 <= state && state <= 0xFFFF) { | |
// Read bytes from uncompressed block | |
int toRead = Math.min(state, len - result); | |
readBytes(b, off + result, toRead); | |
updateMark(dictionaryIndex, toRead); | |
for (int i = 0; i < toRead; i++) { | |
dictionary[dictionaryIndex] = b[off + result]; | |
dictionaryIndex = (dictionaryIndex + 1) & DICTIONARY_MASK; | |
result++; | |
} | |
state -= toRead; | |
return result; | |
} else if (state == -1) // Decode symbols from Huffman-coded block | |
return result + readInsideHuffmanBlock(b, off + result, len - result); | |
else if (state == -4) { | |
// Modification for Eclipse MAT | |
int n = Math.min(inputBufferLength - inputBufferIndex + (inputBitBufferLength >> 3), len - result); | |
if (n > 0) { | |
readBytes(b, off + result, n); | |
result += n; | |
} else if (result < len) { | |
n = len - result; | |
int r = in.read(b, off + result, n); | |
if (r >= 0) { | |
result += r; | |
} else if (result == 0) | |
result = r; | |
} | |
return result; | |
} else | |
throw new AssertionError("Impossible state"); | |
} | |
/** | |
* Notes the current position of the output, so that later the caller can | |
* go back to this spot by calling reset. | |
* This uses the dictionary array as a cache of the last bytes returned to the caller. | |
* The dictionary is 32768 bytes in size, however when reading compressed data | |
* the inflator can generate an extra 257 bytes which are not returned to the caller | |
* but are stored in the dictionary. There is therefore a limit of 32768 - 257 = 32211 bytes | |
* that can be stored. | |
* Strictly speaking, the InputStream contract expects any value of limit to be | |
* honoured, and for implementation to add extra buffering to achieve this. | |
* Addition for Eclipse MAT. | |
* @param limit the caller can read at least this many bytes before calling {@link #reset()} | |
* @see #reset() | |
*/ | |
@Override | |
public void mark(int limit) | |
{ | |
if (limit > DICTIONARY_LENGTH - OUTPUT_BUFFER_LENGTH) | |
throw new IllegalArgumentException(String.valueOf(limit)); | |
if (state == -4) | |
throw new IllegalStateException(String.valueOf(state)); | |
markPos = -2; | |
} | |
/** | |
* Goes back in the output to the point where {@link #mark(int)} was called. | |
* Addition for Eclipse MAT | |
* @throws IOException if it is not possible to go back to the mark point. | |
* The current position is then unchanged. | |
* @see #mark(int) | |
*/ | |
@Override | |
public void reset() throws IOException | |
{ | |
// If we are between chunks then reset doesn't make sense at it | |
// only operates on decompressed data. | |
if (state == -4) | |
throw new IllegalStateException(String.valueOf(state)); | |
if (markPos >= 0) { | |
int toread; | |
if (dictionaryIndex > markPos) | |
toread = dictionaryIndex - markPos; | |
else | |
toread = DICTIONARY_LENGTH - markPos + dictionaryIndex; | |
if (outputBufferLength > toread) { | |
assert outputBufferIndex >= outputBufferLength - toread; | |
outputBufferIndex = outputBufferLength - toread; | |
} else { | |
outputBufferLength = toread; | |
outputBufferIndex = 0; | |
} | |
} else if (markPos == -3) | |
throw new IOException("too far"); | |
else if (markPos == -2) | |
{ | |
// mark() immediately followed by reset() so we don't need to do anything | |
// mark is still valid for a subsequent reset. | |
} | |
else if (markPos == -1) | |
throw new IOException("no mark"); | |
else | |
throw new IllegalStateException(String.valueOf(markPos)); | |
} | |
/** | |
* Does this stream support {@link #mark(int)} and {@link #reset()} ? | |
* It does. | |
* Addition for Eclipse MAT. | |
* @return true | |
* @see #mark(int) | |
* @see #reset() | |
*/ | |
@Override | |
public boolean markSupported() | |
{ | |
return true; | |
} | |
/** | |
* The number of bytes which can be read without blocking. | |
* With the underlying stream it is not clear how many bytes those | |
* will expand to so just rely on what is in the buffers. | |
* Addition for Eclipse MAT. | |
* @return the number of bytes which can be read without blocking. | |
*/ | |
public int available() throws IOException { | |
long input; | |
int actual = (outputBufferLength - outputBufferIndex); | |
if (state == -4) { | |
// pass through mode | |
input = inputBufferLength - inputBufferIndex + (inputBitBufferLength >> 3); | |
} else { | |
// The amount of compressed data | |
input = inputBufferLength - inputBufferIndex + (inputBitBufferLength >> 3); | |
if (state > 0) { | |
// Uncompressed block | |
if (input > state) | |
input = state; | |
} else if (state == -1 && input >= 3) { | |
// With 48 bits of input we can decode at least one symbol | |
// However, the symbol could be end of block! | |
input = 0; | |
} else { | |
// We don't know how much can be read without going to the underlying stream | |
input = 0; | |
} | |
} | |
return (int) Math.min(actual + input, Integer.MAX_VALUE); | |
} | |
//@Override | |
public long skip(long n) throws IOException | |
{ | |
if (n <= 0) | |
return 0; | |
long skipped = 0; | |
if (outputBufferLength >= 0) { | |
// There could be a new mark while we are re-reading data, or reading duplicated bytes. | |
if (markPos == -2) | |
markPos = (dictionaryIndex - (outputBufferLength - outputBufferIndex)) & DICTIONARY_MASK; | |
if (n >= outputBufferLength - outputBufferIndex) { | |
skipped = outputBufferLength - outputBufferIndex; | |
outputBufferLength = 0; | |
outputBufferIndex = 0; | |
n -= skipped; | |
} else { | |
outputBufferIndex += n; | |
return n; | |
} | |
} | |
byte buf[] = null; | |
while (n > 0) | |
{ | |
int toskip = (int)Math.min(n, 16384); | |
if (buf == null) | |
buf = new byte[toskip]; | |
long r = read(buf, 0, toskip); | |
if (r == -1) | |
{ | |
break; | |
} | |
skipped += r; | |
n -= r; | |
} | |
return skipped; | |
} | |
/** | |
* See if too much data has been read for a {@link #reset()} to not work anymore. | |
* Addition for Eclipse MAT. | |
* @param dictionaryIndex the next place to store in the dictionary | |
* @param count the number of bytes to add to dictionary | |
*/ | |
private void updateMark(int dictionaryIndex, int count) | |
{ | |
if (markPos == -1) | |
return; | |
else if (markPos == -2) { | |
if (count <= DICTIONARY_LENGTH) | |
markPos = dictionaryIndex; | |
else | |
markPos = -3; | |
} else if (count >= DICTIONARY_LENGTH) | |
markPos = -3; | |
else if (markPos >= dictionaryIndex) | |
if (dictionaryIndex + count >= markPos) | |
markPos = -3; | |
else if (((dictionaryIndex + count) & DICTIONARY_MASK) >= markPos) | |
markPos = -3; | |
} | |
private void endofblock() { | |
if (isLastBlock && !(markPos >= 0 || outputBufferLength > 0)) { | |
// Clear the dictionary early | |
dictionary = null; | |
dictionaryIndex = -1; | |
} | |
} | |
// This method exists to split up the public read(byte[],int,int) method that is rather long. | |
// For internal use only; must only be called by read(byte[],int,int). The input array's subrange must be valid | |
// (the caller checks the preconditions). The current state must be -1. This returns a number in the range [0, len]. | |
private int readInsideHuffmanBlock(byte[] b, int off, int len) throws IOException { | |
int result = 0; | |
while (result < len) { | |
// Try to fill the input bit buffer (somewhat similar to logic in readBits()) | |
if (inputBitBufferLength < 48) { | |
byte[] c = inputBuffer; // Shorter name | |
int i = inputBufferIndex; // Shorter name | |
int numBytes = Math.min((64 - inputBitBufferLength) >>> 3, inputBufferLength - i); | |
assert 0 <= numBytes && numBytes <= 8; | |
if (numBytes == 2) { // Only implement special cases that occur frequently in practice | |
inputBitBuffer |= (long)((c[i]&0xFF) | (c[i+1]&0xFF)<<8) << inputBitBufferLength; | |
inputBitBufferLength += 2 * 8; | |
inputBufferIndex += 2; | |
} else if (numBytes == 3) { | |
inputBitBuffer |= (long)((c[i]&0xFF) | (c[i+1]&0xFF)<<8 | (c[i+2]&0xFF)<<16) << inputBitBufferLength; | |
inputBitBufferLength += 3 * 8; | |
inputBufferIndex += 3; | |
} else if (numBytes == 4) { | |
inputBitBuffer |= (((c[i]&0xFF) | (c[i+1]&0xFF)<<8 | (c[i+2]&0xFF)<<16 | c[i+3]<<24) & 0xFFFFFFFFL) << inputBitBufferLength; | |
inputBitBufferLength += 4 * 8; | |
inputBufferIndex += 4; | |
} else { // This slower general logic is valid for 0 <= numBytes <= 8 | |
for (int j = 0; j < numBytes; j++, inputBitBufferLength += 8, inputBufferIndex++) | |
inputBitBuffer |= (c[inputBufferIndex] & 0xFFL) << inputBitBufferLength; | |
} | |
} | |
// The worst-case number of bits consumed in one iteration: | |
// length (symbol (15) + extra (5)) + distance (symbol (15) + extra (13)) = 48. | |
// This allows us to do decoding entirely from the bit buffer, avoiding the byte buffer or actual I/O. | |
if (inputBitBufferLength >= 48) { // Fast path | |
// Decode next literal/length symbol (a customized version of decodeSymbol()) | |
int sym; | |
{ | |
int temp = literalLengthCodeTable[(int)inputBitBuffer & CODE_TABLE_MASK]; | |
assert temp >= 0; // No need to mask off sign extension bits | |
int consumed = temp >>> 11; | |
inputBitBuffer >>>= consumed; | |
inputBitBufferLength -= consumed; | |
int node = (temp << 21) >> 21; // Sign extension from 11 bits | |
while (node >= 0) { | |
node = literalLengthCodeTree[node + ((int)inputBitBuffer & 1)]; | |
inputBitBuffer >>>= 1; | |
inputBitBufferLength--; | |
} | |
sym = ~node; | |
} | |
// Handle the symbol by ranges | |
assert 0 <= sym && sym <= 287; | |
if (sym < 256) { // Literal byte | |
b[off + result] = (byte)sym; | |
updateMark(dictionaryIndex, 1); | |
dictionary[dictionaryIndex] = (byte)sym; | |
dictionaryIndex = (dictionaryIndex + 1) & DICTIONARY_MASK; | |
result++; | |
} else if (sym > 256) { // Length and distance for copying | |
// Decode the run length (a customized version of decodeRunLength()) | |
assert 257 <= sym && sym <= 287; | |
if (sym > 285) | |
destroyAndThrow(new DataFormatException("Reserved run length symbol: " + sym)); | |
int run; | |
{ | |
int temp = RUN_LENGTH_TABLE[sym - 257]; | |
run = temp >>> 3; | |
int numExtraBits = temp & 7; | |
run += (int)inputBitBuffer & ((1 << numExtraBits) - 1); | |
inputBitBuffer >>>= numExtraBits; | |
inputBitBufferLength -= numExtraBits; | |
} | |
assert 3 <= run && run <= 258; | |
// Decode next distance symbol (a customized version of decodeSymbol()) | |
if (distanceCodeTree == null) | |
destroyAndThrow(new DataFormatException("Length symbol encountered with empty distance code")); | |
int distSym; | |
{ | |
int temp = distanceCodeTable[(int)inputBitBuffer & CODE_TABLE_MASK]; | |
assert temp >= 0; // No need to mask off sign extension bits | |
int consumed = temp >>> 11; | |
inputBitBuffer >>>= consumed; | |
inputBitBufferLength -= consumed; | |
int node = (temp << 21) >> 21; // Sign extension from 11 bits | |
while (node >= 0) { // Medium path | |
node = distanceCodeTree[node + ((int)inputBitBuffer & 1)]; | |
inputBitBuffer >>>= 1; | |
inputBitBufferLength--; | |
} | |
distSym = ~node; | |
} | |
assert 0 <= distSym && distSym <= 31; | |
// Decode the distance (a customized version of decodeDistance()) | |
if (distSym > 29) | |
destroyAndThrow(new DataFormatException("Reserved distance symbol: " + distSym)); | |
int dist; | |
{ | |
int temp = DISTANCE_TABLE[distSym]; | |
dist = temp >>> 4; | |
int numExtraBits = temp & 0xF; | |
dist += (int)inputBitBuffer & ((1 << numExtraBits) - 1); | |
inputBitBuffer >>>= numExtraBits; | |
inputBitBufferLength -= numExtraBits; | |
} | |
assert 1 <= dist && dist <= 32768; | |
assert inputBitBufferLength >= 0; | |
// Copy bytes to output and dictionary | |
int dictReadIndex = (dictionaryIndex - dist) & DICTIONARY_MASK; | |
if (len - result >= run) { // Nice case with less branching | |
updateMark(dictionaryIndex, run); | |
for (int i = 0; i < run; i++) { | |
byte bb = dictionary[dictReadIndex]; | |
dictionary[dictionaryIndex] = bb; | |
b[off + result] = bb; | |
dictReadIndex = (dictReadIndex + 1) & DICTIONARY_MASK; | |
dictionaryIndex = (dictionaryIndex + 1) & DICTIONARY_MASK; | |
result++; | |
} | |
} else { // General case | |
assert outputBufferLength == 0; | |
updateMark(dictionaryIndex, run); | |
for (int i = 0; i < run; i++) { | |
byte bb = dictionary[dictReadIndex]; | |
dictionary[dictionaryIndex] = bb; | |
dictReadIndex = (dictReadIndex + 1) & DICTIONARY_MASK; | |
dictionaryIndex = (dictionaryIndex + 1) & DICTIONARY_MASK; | |
if (result < len) { | |
b[off + result] = bb; | |
result++; | |
} else { | |
assert outputBufferLength < DICTIONARY_LENGTH; | |
assert outputBufferLength < OUTPUT_BUFFER_LENGTH; | |
outputBufferLength++; | |
} | |
} | |
} | |
} else { // sym == 256, end of block | |
literalLengthCodeTree = null; | |
literalLengthCodeTable = null; | |
distanceCodeTree = null; | |
distanceCodeTable = null; | |
state = 0; | |
endofblock(); | |
break; | |
} | |
} else { // General case (always correct), when not enough bits in buffer to guarantee reading | |
int sym = decodeSymbol(literalLengthCodeTree); | |
assert 0 <= sym && sym <= 287; | |
if (sym < 256) { // Literal byte | |
b[off + result] = (byte)sym; | |
updateMark(dictionaryIndex, 1); | |
dictionary[dictionaryIndex] = (byte)sym; | |
dictionaryIndex = (dictionaryIndex + 1) & DICTIONARY_MASK; | |
result++; | |
} else if (sym > 256) { // Length and distance for copying | |
int run = decodeRunLength(sym); | |
assert 3 <= run && run <= 258; | |
if (distanceCodeTree == null) | |
destroyAndThrow(new DataFormatException("Length symbol encountered with empty distance code")); | |
int distSym = decodeSymbol(distanceCodeTree); | |
assert 0 <= distSym && distSym <= 31; | |
int dist = decodeDistance(distSym); | |
assert 1 <= dist && dist <= 32768; | |
assert outputBufferLength == 0; | |
// Copy bytes to output and dictionary | |
int dictReadIndex = (dictionaryIndex - dist) & DICTIONARY_MASK; | |
updateMark(dictionaryIndex, run); | |
for (int i = 0; i < run; i++) { | |
byte bb = dictionary[dictReadIndex]; | |
dictReadIndex = (dictReadIndex + 1) & DICTIONARY_MASK; | |
dictionary[dictionaryIndex] = bb; | |
dictionaryIndex = (dictionaryIndex + 1) & DICTIONARY_MASK; | |
if (result < len) { | |
b[off + result] = bb; | |
result++; | |
} else { | |
assert outputBufferLength < DICTIONARY_LENGTH; | |
assert outputBufferLength < OUTPUT_BUFFER_LENGTH; | |
outputBufferLength++; | |
} | |
} | |
} else { // sym == 256, end of block | |
literalLengthCodeTree = null; | |
literalLengthCodeTable = null; | |
distanceCodeTree = null; | |
distanceCodeTable = null; | |
state = 0; | |
endofblock(); | |
break; | |
} | |
} | |
} | |
return result; | |
} | |
/** | |
* Detaches the underlying input stream from this decompressor. This puts the underlying stream | |
* at the position of the first byte after the data that this decompressor actually consumed. | |
* Calling {@code detach()} invalidates this stream object but doesn't close the underlying stream. | |
* <p>This method exists because for efficiency, the decompressor may read more bytes from the | |
* underlying stream than necessary to produce the decompressed data. If you want to continue | |
* reading the underlying stream exactly after the point the DEFLATE-compressed data ends, | |
* then it is necessary to call this detach method.</p> | |
* <p>This can only be called once, and is mutually exclusive with respect to calling | |
* {@link #close()}. It is illegal to call {@link #read()} after detaching.</p> | |
* @throws IllegalStateException if detach was already called or this stream has been closed | |
* @throws IOException if an I/O exception occurred | |
*/ | |
public void detach() throws IOException { | |
// Modifications for Eclipse MAT | |
//if (!isDetachable) | |
// throw new IllegalStateException("Detachability not specified at construction"); | |
if (in == null) | |
throw new IllegalStateException("Input stream already detached/closed"); | |
if (exception != null) | |
throw exception; | |
if (!isDetachable) | |
{ | |
if (state == -4) | |
throw new IllegalStateException("Input stream already detached"); | |
endofblock(); | |
state = -4; | |
return; | |
} | |
// Rewind the underlying stream, then skip over bytes that were already consumed. | |
// Note that a byte with some bits consumed is considered to be fully consumed. | |
in.reset(); | |
int skip = inputBufferIndex - inputBitBufferLength / 8; | |
assert skip >= 0; | |
while (skip > 0) { | |
long n = in.skip(skip); | |
if (n <= 0) | |
throw new EOFException(); | |
skip -= n; | |
} | |
in = null; | |
state = -3; | |
destroyState(); | |
} | |
/** | |
* Resume decompression. | |
* Addition for Eclipse MAT. | |
*/ | |
public void attach() | |
{ | |
if (state != -4) | |
throw new IllegalStateException("Not detached"); | |
// Keep the old dictionary if it is needed for reset() | |
// Could clear it (but no point) if (!(markPos >= 0 || outputBufferLength > 0)) | |
if (dictionary == null) { | |
// The dictionary was cleared earlier | |
dictionary = new byte[DICTIONARY_LENGTH]; | |
dictionaryIndex = 0; | |
} | |
isLastBlock = false; | |
state = 0; | |
} | |
public String toString() { | |
return this.getClass()+" "+state+" "+(dictionary == null ? "no buf" : dictionary); | |
} | |
/** | |
* Closes this input stream and the underlying stream. | |
* It is illegal to call {@link #read()} or {@link #detach()} after closing. | |
* It is idempotent to call this {@link #close()} method more than once. | |
* @throws IOException if an I/O exception occurred in the underlying stream | |
*/ | |
public void close() throws IOException { | |
if (in == null) | |
return; | |
super.close(); | |
in = null; | |
state = -3; | |
exception = null; | |
destroyState(); | |
} | |
/*---- Huffman coding methods ----*/ | |
// Reads the current block's dynamic Huffman code tables from from the input buffers/stream, | |
// processes the code lengths and computes the code trees, and ultimately sets just the variables | |
// {literalLengthCodeTree, literalLengthCodeTable, distanceCodeTree, distanceCodeTable}. | |
// This might throw an IOException for actual I/O exceptions, unexpected end of stream, | |
// or a description of an invalid Huffman code. | |
private void decodeHuffmanCodes() throws IOException { | |
int numLitLenCodes = readBits(5) + 257; // hlit + 257 | |
int numDistCodes = readBits(5) + 1; // hdist + 1 | |
// Read the code length code lengths | |
int numCodeLenCodes = readBits(4) + 4; // hclen + 4 | |
byte[] codeLenCodeLen = new byte[19]; // This array is filled in a strange order | |
for (int i = 0; i < numCodeLenCodes; i++) | |
codeLenCodeLen[CODE_LENGTH_CODE_ORDER[i]] = (byte)readBits(3); | |
short[] codeLenCodeTree; | |
try { | |
codeLenCodeTree = codeLengthsToCodeTree(codeLenCodeLen); | |
} catch (DataFormatException e) { | |
destroyAndThrow(e); | |
throw new AssertionError("Unreachable"); | |
} | |
// Read the main code lengths and handle runs | |
byte[] codeLens = new byte[numLitLenCodes + numDistCodes]; | |
byte runVal = -1; | |
int runLen = 0; | |
for (int i = 0; i < codeLens.length; ) { | |
if (runLen > 0) { | |
assert runVal != -1; | |
codeLens[i] = runVal; | |
runLen--; | |
i++; | |
} else { | |
int sym = decodeSymbol(codeLenCodeTree); | |
assert 0 <= sym && sym <= 18; | |
if (sym < 16) { | |
runVal = codeLens[i] = (byte)sym; | |
i++; | |
} else if (sym == 16) { | |
if (runVal == -1) | |
destroyAndThrow(new DataFormatException("No code length value to copy")); | |
runLen = readBits(2) + 3; | |
} else if (sym == 17) { | |
runVal = 0; | |
runLen = readBits(3) + 3; | |
} else { // sym == 18 | |
runVal = 0; | |
runLen = readBits(7) + 11; | |
} | |
} | |
} | |
if (runLen > 0) | |
destroyAndThrow(new DataFormatException("Run exceeds number of codes")); | |
// Create literal-length code tree | |
byte[] litLenCodeLen = Arrays.copyOf(codeLens, numLitLenCodes); | |
try { | |
literalLengthCodeTree = codeLengthsToCodeTree(litLenCodeLen); | |
} catch (DataFormatException e) { | |
destroyAndThrow(e); | |
throw new AssertionError("Unreachable"); | |
} | |
literalLengthCodeTable = codeTreeToCodeTable(literalLengthCodeTree); | |
// Create distance code tree with some extra processing | |
byte[] distCodeLen = Arrays.copyOfRange(codeLens, numLitLenCodes, codeLens.length); | |
if (distCodeLen.length == 1 && distCodeLen[0] == 0) | |
distanceCodeTree = null; // Empty distance code; the block shall be all literal symbols | |
else { | |
// Get statistics for upcoming logic | |
int oneCount = 0; | |
int otherPositiveCount = 0; | |
for (byte x : distCodeLen) { | |
if (x == 1) | |
oneCount++; | |
else if (x > 1) | |
otherPositiveCount++; | |
} | |
// Handle the case where only one distance code is defined | |
if (oneCount == 1 && otherPositiveCount == 0) { | |
// Add a dummy invalid code to make the Huffman tree complete | |
distCodeLen = Arrays.copyOf(distCodeLen, 32); | |
distCodeLen[31] = 1; | |
} | |
try { | |
distanceCodeTree = codeLengthsToCodeTree(distCodeLen); | |
} catch (DataFormatException e) { | |
destroyAndThrow(e); | |
throw new AssertionError("Unreachable"); | |
} | |
distanceCodeTable = codeTreeToCodeTable(distanceCodeTree); | |
} | |
} | |
/* | |
* Converts the given array of symbol code lengths into a canonical code tree. | |
* A symbol code length is either zero (absent from the tree) or a positive integer. | |
* | |
* A code tree is an array of integers, where each pair represents a node. | |
* Each pair is adjacent and starts on an even index. The first element of | |
* the pair represents the left child and the second element represents the | |
* right child. The root node is at index 0. If an element is non-negative, | |
* then it is the index of the child node in the array. Otherwise it is the | |
* bitwise complement of the leaf symbol. This tree is used in decodeSymbol() | |
* and codeTreeToCodeTable(). Not every element of the array needs to be | |
* used, nor do used elements need to be contiguous. | |
* | |
* For example, this Huffman tree: | |
* o | |
* / \ | |
* o \ | |
* / \ \ | |
* 'a' 'b' 'c' | |
* is serialized as this array: | |
* {2, ~'c', ~'a', ~'b'} | |
* because the root is located at index 0 and the other internal node is | |
* located at index 2. | |
*/ | |
private static short[] codeLengthsToCodeTree(byte[] codeLengths) throws DataFormatException { | |
// Allocate array for the worst case if all symbols are present | |
short[] result = new short[(codeLengths.length - 1) * 2]; | |
Arrays.fill(result, CODE_TREE_UNUSED_SLOT); | |
result[0] = CODE_TREE_OPEN_SLOT; | |
result[1] = CODE_TREE_OPEN_SLOT; | |
int allocated = 2; // Always even in this algorithm | |
int maxCodeLen = 0; | |
for (byte cl : codeLengths) { | |
assert 0 <= cl && cl <= 15; | |
maxCodeLen = Math.max(cl, maxCodeLen); | |
} | |
if (maxCodeLen > 15) | |
throw new AssertionError("Maximum code length exceeds DEFLATE specification"); | |
// Allocate Huffman tree nodes according to ascending code lengths | |
for (int curCodeLen = 1; curCodeLen <= maxCodeLen; curCodeLen++) { | |
// Loop invariant: Each OPEN child slot in the result array has depth curCodeLen | |
// Allocate all symbols of current code length to open slots in ascending order | |
int resultIndex = 0; | |
for (int symbol = 0; ; ) { | |
// Find next symbol having current code length | |
while (symbol < codeLengths.length && codeLengths[symbol] != curCodeLen) | |
symbol++; | |
if (symbol == codeLengths.length) | |
break; // No more symbols to process | |
// Find next open child slot | |
while (resultIndex < allocated && result[resultIndex] != CODE_TREE_OPEN_SLOT) | |
resultIndex++; | |
if (resultIndex == allocated) // No more slots left | |
throw new DataFormatException("Canonical code fails to produce full Huffman code tree"); | |
// Put the symbol into the slot and increment | |
result[resultIndex] = (short)~symbol; | |
resultIndex++; | |
symbol++; | |
} | |
// Take all open slots and deepen them by one level | |
for (int end = allocated; resultIndex < end; resultIndex++) { | |
if (result[resultIndex] == CODE_TREE_OPEN_SLOT) { | |
// Allocate a new node | |
assert allocated + 2 <= result.length; | |
result[resultIndex] = (short)allocated; | |
result[allocated + 0] = CODE_TREE_OPEN_SLOT; | |
result[allocated + 1] = CODE_TREE_OPEN_SLOT; | |
allocated += 2; | |
} | |
} | |
} | |
// Check for unused open slots after all symbols are allocated | |
for (int i = 0; i < allocated; i++) { | |
if (result[i] == CODE_TREE_OPEN_SLOT) | |
throw new DataFormatException("Canonical code fails to produce full Huffman code tree"); | |
} | |
return result; | |
} | |
/* | |
* Converts a code tree array into a fast look-up table that consumes up to | |
* CODE_TABLE_BITS at once. Each entry i in the table encodes the result of | |
* decoding starting from the root and consuming the bits of i starting from | |
* the lowest-order bits. | |
* | |
* Each array element encodes (numBitsConsumed << 11) | (node & 0x7FF), where: | |
* - numBitsConsumed is a 4-bit unsigned integer in the range [1, CODE_TABLE_BITS]. | |
* - node is an 11-bit signed integer representing either the current node | |
* (which is a non-negative number) after consuming all the available bits | |
* from i, or the bitwise complement of the decoded symbol (so it's negative). | |
* Note that each element is a non-negative number. | |
*/ | |
private static short[] codeTreeToCodeTable(short[] codeTree) { | |
assert 1 <= CODE_TABLE_BITS && CODE_TABLE_BITS <= 15; | |
short[] result = new short[1 << CODE_TABLE_BITS]; | |
for (int i = 0; i < result.length; i++) { | |
// Simulate decodeSymbol() using the bits of i | |
int node = 0; | |
int consumed = 0; | |
do { | |
node = codeTree[node + ((i >>> consumed) & 1)]; | |
consumed++; | |
} while (node >= 0 && consumed < CODE_TABLE_BITS); | |
assert 1 <= consumed && consumed <= 15; // 4 bits unsigned | |
assert -1024 <= node && node <= 1023; // 11 bits signed | |
result[i] = (short)(consumed << 11 | (node & 0x7FF)); | |
assert result[i] >= 0; | |
} | |
return result; | |
} | |
// Reads bits from the input buffers/stream and uses the given code tree to decode the next symbol. | |
// The returned symbol value is a non-negative integer. This throws an IOException if the end of stream | |
// is reached before a symbol is decoded, or if the underlying stream experiences an I/O exception. | |
private int decodeSymbol(short[] codeTree) throws IOException { | |
int node = 0; // An index into the codeTree array which signifies the current tree node | |
while (node >= 0) { | |
if (inputBitBufferLength > 0) { // Medium path using buffered bits | |
node = codeTree[node + ((int)inputBitBuffer & 1)]; | |
inputBitBuffer >>>= 1; | |
inputBitBufferLength--; | |
} else // Slow path with potential I/O operations | |
node = codeTree[node + readBits(1)]; | |
} | |
return ~node; // Symbol was encoded as bitwise complement | |
} | |
// Takes the given run length symbol in the range [257, 287], possibly reads some more input bits, | |
// and returns a number in the range [3, 258]. This throws an IOException if bits needed to be read | |
// but the end of stream was reached or the underlying stream experienced an I/O exception. | |
private int decodeRunLength(int sym) throws IOException { | |
assert 257 <= sym && sym <= 287; | |
if (sym <= 264) | |
return sym - 254; | |
else if (sym <= 284) { | |
int numExtraBits = (sym - 261) >>> 2; | |
return ((((sym - 1) & 3) | 4) << numExtraBits) + 3 + readBits(numExtraBits); | |
} else if (sym == 285) | |
return 258; | |
else { // sym is 286 or 287 | |
destroyAndThrow(new DataFormatException("Reserved run length symbol: " + sym)); | |
throw new AssertionError("Unreachable"); | |
} | |
} | |
// Takes the given run length symbol in the range [0, 31], possibly reads some more input bits, | |
// and returns a number in the range [1, 32768]. This throws an IOException if bits needed to | |
// be read but the end of stream was reached or the underlying stream experienced an I/O exception. | |
private int decodeDistance(int sym) throws IOException { | |
assert 0 <= sym && sym <= 31; | |
if (sym <= 3) | |
return sym + 1; | |
else if (sym <= 29) { | |
int numExtraBits = (sym >>> 1) - 1; | |
return (((sym & 1) | 2) << numExtraBits) + 1 + readBits(numExtraBits); | |
} else { // sym is 30 or 31 | |
destroyAndThrow(new DataFormatException("Reserved distance symbol: " + sym)); | |
throw new AssertionError("Unreachable"); | |
} | |
} | |
/*---- I/O methods ----*/ | |
// Returns the given number of least significant bits from the bit buffer. | |
// This updates the bit buffer state and possibly also the byte buffer state. | |
private int readBits(int numBits) throws IOException { | |
// Check arguments and invariants | |
assert 1 <= numBits && numBits <= 16; // Note: DEFLATE uses up to 16, but this method is correct up to 31 | |
assert 0 <= inputBitBufferLength && inputBitBufferLength <= 63; | |
assert inputBitBuffer >>> inputBitBufferLength == 0; // Ensure high-order bits are clean | |
// Ensure there is enough data in the bit buffer to satisfy the request | |
while (inputBitBufferLength < numBits) { | |
while (inputBufferIndex >= inputBufferLength) // Fill and retry | |
fillInputBuffer(); | |
// Pack as many bytes as possible from input byte buffer into the bit buffer | |
int numBytes = Math.min((64 - inputBitBufferLength) >>> 3, inputBufferLength - inputBufferIndex); | |
if (numBytes <= 0) | |
throw new AssertionError("Impossible state"); | |
for (int i = 0; i < numBytes; i++, inputBitBufferLength += 8, inputBufferIndex++) | |
inputBitBuffer |= (inputBuffer[inputBufferIndex] & 0xFFL) << inputBitBufferLength; | |
assert inputBitBufferLength <= 64; // Can temporarily be 64 | |
} | |
// Extract the bits to return | |
int result = (int)inputBitBuffer & ((1 << numBits) - 1); | |
inputBitBuffer >>>= numBits; | |
inputBitBufferLength -= numBits; | |
// Check return and recheck invariants | |
assert result >>> numBits == 0; | |
assert 0 <= inputBitBufferLength && inputBitBufferLength <= 63; | |
assert inputBitBuffer >>> inputBitBufferLength == 0; | |
return result; | |
} | |
// Reads exactly 'len' bytes from {the input buffers or underlying input stream} into the | |
// given array subrange. This method alters the input buffer states, may throw an IOException, | |
// and would destroy the decompressor state if EOF occurs before the read length is satisfied. | |
private void readBytes(byte[] b, int off, int len) throws IOException { | |
// Check bit buffer invariants | |
if (inputBitBufferLength < 0 || inputBitBufferLength > 63 | |
|| inputBitBuffer >>> inputBitBufferLength != 0) | |
throw new AssertionError("Invalid input bit buffer state"); | |
// First unpack saved bits | |
alignInputToByte(); | |
for (; len > 0 && inputBitBufferLength >= 8; off++, len--) { | |
b[off] = (byte)inputBitBuffer; | |
inputBitBuffer >>>= 8; | |
inputBitBufferLength -= 8; | |
} | |
// Read from input buffer | |
{ | |
int n = Math.min(len, inputBufferLength - inputBufferIndex); | |
assert inputBitBufferLength == 0 || n == 0; | |
System.arraycopy(inputBuffer, inputBufferIndex, b, off, n); | |
inputBufferIndex += n; | |
off += n; | |
len -= n; | |
} | |
// Read directly from input stream (without putting into input buffer) | |
while (len > 0) { | |
assert inputBufferIndex == inputBufferLength; | |
int n = in.read(b, off, len); | |
if (n == -1) | |
destroyAndThrow(new EOFException("Unexpected end of stream")); | |
off += n; | |
len -= n; | |
} | |
} | |
// Fills the input byte buffer with new data read from the underlying input stream. | |
// Requires the buffer to be fully consumed before being called. This method sets | |
// inputBufferLength to a value in the range [-1, inputBuffer.length] and inputBufferIndex to 0. | |
private void fillInputBuffer() throws IOException { | |
if (state < -1) | |
throw new AssertionError("Must not read in this state"); | |
if (inputBufferIndex < inputBufferLength) | |
throw new AssertionError("Input buffer not fully consumed yet"); | |
if (isDetachable) | |
in.mark(inputBuffer.length); | |
inputBufferLength = in.read(inputBuffer); | |
inputBufferIndex = 0; | |
if (inputBufferLength == -1) | |
destroyAndThrow(new EOFException("Unexpected end of stream")); // Note: This sets inputBufferLength to 0 | |
if (inputBufferLength < -1 || inputBufferLength > inputBuffer.length) | |
throw new AssertionError(); | |
} | |
// Discards the remaining bits (0 to 7) in the current byte being read, if any. Always succeeds. | |
private void alignInputToByte() { | |
int discard = inputBitBufferLength & 7; | |
inputBitBuffer >>>= discard; | |
inputBitBufferLength -= discard; | |
assert inputBitBufferLength % 8 == 0; | |
} | |
/*---- State management methods ----*/ | |
// Throws an IOException with the given reason, and destroys the state of this decompressor. | |
private void destroyAndThrow(IOException e) throws IOException { | |
state = -2; | |
exception = e; | |
destroyState(); | |
// Do not set 'in' to null, so that calling close() is still possible | |
throw e; | |
} | |
// Clears all state variables except {in, state, exception}, to prevent accidental use of the | |
// stream thereafter. It is illegal to call read() or detach() after this method is called. | |
// The caller is responsible for manipulating the other state variables appropriately. | |
private void destroyState() { | |
isLastBlock = true; | |
literalLengthCodeTree = null; | |
literalLengthCodeTable = null; | |
distanceCodeTree = null; | |
distanceCodeTable = null; | |
inputBuffer = null; | |
inputBufferLength = 0; | |
inputBufferIndex = 0; | |
inputBitBuffer = 0; | |
inputBitBufferLength = 0; | |
outputBufferLength = 0; | |
outputBufferIndex = 0; | |
dictionary = null; | |
dictionaryIndex = 0; | |
} | |
/*---- Constants and tables ----*/ | |
private static final int[] CODE_LENGTH_CODE_ORDER = | |
{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; | |
private static final short[] FIXED_LITERAL_LENGTH_CODE_TREE; | |
private static final short[] FIXED_LITERAL_LENGTH_CODE_TABLE; | |
private static final short[] FIXED_DISTANCE_CODE_TREE; | |
private static final short[] FIXED_DISTANCE_CODE_TABLE; | |
// For use in codeLengthsToCodeTree() only. | |
private static final short CODE_TREE_UNUSED_SLOT = 0x7000; | |
private static final short CODE_TREE_OPEN_SLOT = 0x7002; | |
// Any integer from 1 to 15 is valid. Affects speed but produces same output. | |
private static final int CODE_TABLE_BITS = 9; | |
private static final int CODE_TABLE_MASK = (1 << CODE_TABLE_BITS) - 1; | |
static { | |
byte[] llcodelens = new byte[288]; | |
Arrays.fill(llcodelens, 0, 144, (byte)8); | |
Arrays.fill(llcodelens, 144, 256, (byte)9); | |
Arrays.fill(llcodelens, 256, 280, (byte)7); | |
Arrays.fill(llcodelens, 280, 288, (byte)8); | |
byte[] distcodelens = new byte[32]; | |
Arrays.fill(distcodelens, (byte)5); | |
try { | |
FIXED_LITERAL_LENGTH_CODE_TREE = codeLengthsToCodeTree(llcodelens); | |
FIXED_DISTANCE_CODE_TREE = codeLengthsToCodeTree(distcodelens); | |
} catch (DataFormatException e) { | |
throw new AssertionError(e); | |
} | |
FIXED_LITERAL_LENGTH_CODE_TABLE = codeTreeToCodeTable(FIXED_LITERAL_LENGTH_CODE_TREE); | |
FIXED_DISTANCE_CODE_TABLE = codeTreeToCodeTable(FIXED_DISTANCE_CODE_TREE); | |
} | |
// Must be a power of 2. Do not change this constant value. If the value is decreased, then | |
// decompression may produce different data that violates the DEFLATE spec (but no crashes). | |
// If the value is increased, the behavior stays the same but memory is wasted with no benefit. | |
private static final int DICTIONARY_LENGTH = 32 * 1024; | |
// This is why the above must be a power of 2. | |
private static final int DICTIONARY_MASK = DICTIONARY_LENGTH - 1; | |
// The maximum size of extra output that can be generated that might not fit into the | |
// read buffer. This data needs to be saved for later. | |
private static final int OUTPUT_BUFFER_LENGTH = 257; | |
// For length symbols from 257 to 285 (inclusive). RUN_LENGTH_TABLE[i] = | |
// (base of run length) << 3 | (number of extra bits to read). | |
private static final short[] RUN_LENGTH_TABLE = {24, 32, 40, 48, 56, 64, 72, 80, 89, 105, 121, 137, | |
154, 186, 218, 250, 283, 347, 411, 475, 540, 668, 796, 924, 1053, 1309, 1565, 1821, 2064}; | |
// For length symbols from 0 to 29 (inclusive). DISTANCE_TABLE[i] = | |
// (base of distance) << 4 | (number of extra bits to read). | |
private static final int[] DISTANCE_TABLE = {16, 32, 48, 64, 81, 113, 146, 210, 275, 403, 532, 788, 1045, 1557, | |
2070, 3094, 4119, 6167, 8216, 12312, 16409, 24601, 32794, 49178, 65563, 98331, 131100, 196636, 262173, 393245}; | |
} |