/* * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package java.util.zip; import java.io.SequenceInputStream;import java.io.ByteArrayInputStream;import java.io.InputStream;import java.io.IOException;import java.io.EOFException; /** * This class implements a stream filter for reading compressed data in * the GZIP file format. * * @see InflaterInputStream * @author David Connelly * */publicclass GZIPInputStream extends InflaterInputStream { /** * CRC-32 for uncompressed data. */ protected CRC32 crc = new CRC32(); /** * Indicates end of input stream. */ protected boolean eos; private boolean closed = false; /** * Check to make sure that this stream has not been closed */ private void ensureOpen() throws IOException { if (closed) { throw new IOException("Stream closed"); } } /** * Creates a new input stream with the specified buffer size. * @param in the input stream * @param size the input buffer size * * @exception ZipException if a GZIP format error has occurred or the * compression method used is unsupported * @exception IOException if an I/O error has occurred * @exception IllegalArgumentException if size is <= 0 */ public GZIPInputStream(InputStream in, int size) throws IOException { super(in, new Inflater(true), size); usesDefaultInflater = true; readHeader(in); } /** * Creates a new input stream with a default buffer size. * @param in the input stream * * @exception ZipException if a GZIP format error has occurred or the * compression method used is unsupported * @exception IOException if an I/O error has occurred */ public GZIPInputStream(InputStream in) throws IOException { this(in, 512); } /** * Reads uncompressed data into an array of bytes. If <code>len</code> is not * zero, the method will block until some input can be decompressed; otherwise, * no bytes are read and <code>0</code> is returned. * @param buf the buffer into which the data is read * @param off the start offset in the destination array <code>b</code> * @param len the maximum number of bytes read * @return the actual number of bytes read, or -1 if the end of the * compressed input stream is reached * * @exception NullPointerException If <code>buf</code> is <code>null</code>. * @exception IndexOutOfBoundsException If <code>off</code> is negative, * <code>len</code> is negative, or <code>len</code> is greater than * <code>buf.length - off</code> * @exception ZipException if the compressed input data is corrupt. * @exception IOException if an I/O error has occurred. * */ public int read(byte[] buf, int off, int len) throws IOException { ensureOpen(); if (eos) { return -1; } int n = super.read(buf, off, len); if (n == -1) { if (readTrailer()) eos = true; else return this.read(buf, off, len); } else { crc.update(buf, off, n); } return n; } /** * Closes this input stream and releases any system resources associated * with the stream. * @exception IOException if an I/O error has occurred */ public void close() throws IOException { if (!closed) { super.close(); eos = true; closed = true; } } /** * GZIP header magic number. */ public final static int GZIP_MAGIC = 0x8b1f; /* * File header flags. */ private final static int FTEXT = 1; // Extra text private final static int FHCRC = 2; // Header CRC private final static int FEXTRA = 4; // Extra field private final static int FNAME = 8; // File name private final static int FCOMMENT = 16; // File comment /* * Reads GZIP member header and returns the total byte number * of this member header. */ private int readHeader(InputStream this_in) throws IOException { CheckedInputStream in = new CheckedInputStream(this_in, crc); crc.reset(); // Check header magic if (readUShort(in) != GZIP_MAGIC) { throw new ZipException("Not in GZIP format"); } // Check compression method if (readUByte(in) != 8) { throw new ZipException("Unsupported compression method"); } // Read flags int flg = readUByte(in); // Skip MTIME, XFL, and OS fields skipBytes(in, 6); int n = 2 + 2 + 6; // Skip optional extra field if ((flg & FEXTRA) == FEXTRA) { int m = readUShort(in); skipBytes(in, m); n += m + 2; } // Skip optional file name if ((flg & FNAME) == FNAME) { do { n++; } while (readUByte(in) != 0); } // Skip optional file comment if ((flg & FCOMMENT) == FCOMMENT) { do { n++; } while (readUByte(in) != 0); } // Check optional header CRC if ((flg & FHCRC) == FHCRC) { int v = (int)crc.getValue() & 0xffff; if (readUShort(in) != v) { throw new ZipException("Corrupt GZIP header"); } n += 2; } crc.reset(); return n; } /* * Reads GZIP member trailer and returns true if the eos * reached, false if there are more (concatenated gzip * data set) */ private boolean readTrailer() throws IOException { InputStream in = this.in; int n = inf.getRemaining(); if (n > 0) { in = new SequenceInputStream( new ByteArrayInputStream(buf, len - n, n), in); } // Uses left-to-right evaluation order if ((readUInt(in) != crc.getValue()) || // rfc1952; ISIZE is the input size modulo 2^32 (readUInt(in) != (inf.getBytesWritten() & 0xffffffffL))) throw new ZipException("Corrupt GZIP trailer"); // If there are more bytes available in "in" or // the leftover in the "inf" is > 26 bytes: // this.trailer(8) + next.header.min(10) + next.trailer(8) // try concatenated case if (this.in.available() > 0 || n > 26) { int m = 8; // this.trailer try { m += readHeader(in); // next.header } catch (IOException ze) { return true; // ignore any malformed, do nothing } inf.reset(); if (n > m) inf.setInput(buf, len - n + m, n - m); return false; } return true; } /* * Reads unsigned integer in Intel byte order. */ private long readUInt(InputStream in) throws IOException { long s = readUShort(in); return ((long)readUShort(in) << 16) | s; } /* * Reads unsigned short in Intel byte order. */ private int readUShort(InputStream in) throws IOException { int b = readUByte(in); return ((int)readUByte(in) << 8) | b; } /* * Reads unsigned byte. */ private int readUByte(InputStream in) throws IOException { int b = in.read(); if (b == -1) { throw new EOFException(); } if (b < -1 || b > 255) { // Report on this.in, not argument in; see read{Header, Trailer}. throw new IOException(this.in.getClass().getName() + ".read() returned value out of range -1..255: " + b); } return b; } private byte[] tmpbuf = new byte[128]; /* * Skips bytes of input data blocking until all bytes are skipped. * Does not assume that the input stream is capable of seeking. */ private void skipBytes(InputStream in, int n) throws IOException { while (n > 0) { int len = in.read(tmpbuf, 0, n < tmpbuf.length ? n : tmpbuf.length); if (len == -1) { throw new EOFException(); } n -= len; } }}
/*
* Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.zip;
import java.io.SequenceInputStream;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.EOFException;
/**
* This class implements a stream filter for reading compressed data in
* the GZIP file format.
*
* @see InflaterInputStream
* @author David Connelly
*
*/
public
class GZIPInputStream extends InflaterInputStream {
/**
* CRC-32 for uncompressed data.
*/
protected CRC32 crc = new CRC32();
/**
* Indicates end of input stream.
*/
protected boolean eos;
private boolean closed = false;
/**
* Check to make sure that this stream has not been closed
*/
private void ensureOpen() throws IOException {
if (closed) {
throw new IOException("Stream closed");
}
}
/**
* Creates a new input stream with the specified buffer size.
* @param in the input stream
* @param size the input buffer size
*
* @exception ZipException if a GZIP format error has occurred or the
* compression method used is unsupported
* @exception IOException if an I/O error has occurred
* @exception IllegalArgumentException if size is <= 0
*/
public GZIPInputStream(InputStream in, int size) throws IOException {
super(in, new Inflater(true), size);
usesDefaultInflater = true;
readHeader(in);
}
/**
* Creates a new input stream with a default buffer size.
* @param in the input stream
*
* @exception ZipException if a GZIP format error has occurred or the
* compression method used is unsupported
* @exception IOException if an I/O error has occurred
*/
public GZIPInputStream(InputStream in) throws IOException {
this(in, 512);
}
/**
* Reads uncompressed data into an array of bytes. If <code>len</code> is not
* zero, the method will block until some input can be decompressed; otherwise,
* no bytes are read and <code>0</code> is returned.
* @param buf the buffer into which the data is read
* @param off the start offset in the destination array <code>b</code>
* @param len the maximum number of bytes read
* @return the actual number of bytes read, or -1 if the end of the
* compressed input stream is reached
*
* @exception NullPointerException If <code>buf</code> is <code>null</code>.
* @exception IndexOutOfBoundsException If <code>off</code> is negative,
* <code>len</code> is negative, or <code>len</code> is greater than
* <code>buf.length - off</code>
* @exception ZipException if the compressed input data is corrupt.
* @exception IOException if an I/O error has occurred.
*
*/
public int read(byte[] buf, int off, int len) throws IOException {
ensureOpen();
if (eos) {
return -1;
}
int n = super.read(buf, off, len);
if (n == -1) {
if (readTrailer())
eos = true;
else
return this.read(buf, off, len);
} else {
crc.update(buf, off, n);
}
return n;
}
/**
* Closes this input stream and releases any system resources associated
* with the stream.
* @exception IOException if an I/O error has occurred
*/
public void close() throws IOException {
if (!closed) {
super.close();
eos = true;
closed = true;
}
}
/**
* GZIP header magic number.
*/
public final static int GZIP_MAGIC = 0x8b1f;
/*
* File header flags.
*/
private final static int FTEXT = 1; // Extra text
private final static int FHCRC = 2; // Header CRC
private final static int FEXTRA = 4; // Extra field
private final static int FNAME = 8; // File name
private final static int FCOMMENT = 16; // File comment
/*
* Reads GZIP member header and returns the total byte number
* of this member header.
*/
private int readHeader(InputStream this_in) throws IOException {
CheckedInputStream in = new CheckedInputStream(this_in, crc);
crc.reset();
// Check header magic
if (readUShort(in) != GZIP_MAGIC) {
throw new ZipException("Not in GZIP format");
}
// Check compression method
if (readUByte(in) != 8) {
throw new ZipException("Unsupported compression method");
}
// Read flags
int flg = readUByte(in);
// Skip MTIME, XFL, and OS fields
skipBytes(in, 6);
int n = 2 + 2 + 6;
// Skip optional extra field
if ((flg & FEXTRA) == FEXTRA) {
int m = readUShort(in);
skipBytes(in, m);
n += m + 2;
}
// Skip optional file name
if ((flg & FNAME) == FNAME) {
do {
n++;
} while (readUByte(in) != 0);
}
// Skip optional file comment
if ((flg & FCOMMENT) == FCOMMENT) {
do {
n++;
} while (readUByte(in) != 0);
}
// Check optional header CRC
if ((flg & FHCRC) == FHCRC) {
int v = (int)crc.getValue() & 0xffff;
if (readUShort(in) != v) {
throw new ZipException("Corrupt GZIP header");
}
n += 2;
}
crc.reset();
return n;
}
/*
* Reads GZIP member trailer and returns true if the eos
* reached, false if there are more (concatenated gzip
* data set)
*/
private boolean readTrailer() throws IOException {
InputStream in = this.in;
int n = inf.getRemaining();
if (n > 0) {
in = new SequenceInputStream(
new ByteArrayInputStream(buf, len - n, n), in);
}
// Uses left-to-right evaluation order
if ((readUInt(in) != crc.getValue()) ||
// rfc1952; ISIZE is the input size modulo 2^32
(readUInt(in) != (inf.getBytesWritten() & 0xffffffffL)))
throw new ZipException("Corrupt GZIP trailer");
// If there are more bytes available in "in" or
// the leftover in the "inf" is > 26 bytes:
// this.trailer(8) + next.header.min(10) + next.trailer(8)
// try concatenated case
if (this.in.available() > 0 || n > 26) {
int m = 8; // this.trailer
try {
m += readHeader(in); // next.header
} catch (IOException ze) {
return true; // ignore any malformed, do nothing
}
inf.reset();
if (n > m)
inf.setInput(buf, len - n + m, n - m);
return false;
}
return true;
}
/*
* Reads unsigned integer in Intel byte order.
*/
private long readUInt(InputStream in) throws IOException {
long s = readUShort(in);
return ((long)readUShort(in) << 16) | s;
}
/*
* Reads unsigned short in Intel byte order.
*/
private int readUShort(InputStream in) throws IOException {
int b = readUByte(in);
return ((int)readUByte(in) << 8) | b;
}
/*
* Reads unsigned byte.
*/
private int readUByte(InputStream in) throws IOException {
int b = in.read();
if (b == -1) {
throw new EOFException();
}
if (b < -1 || b > 255) {
// Report on this.in, not argument in; see read{Header, Trailer}.
throw new IOException(this.in.getClass().getName()
+ ".read() returned value out of range -1..255: " + b);
}
return b;
}
private byte[] tmpbuf = new byte[128];
/*
* Skips bytes of input data blocking until all bytes are skipped.
* Does not assume that the input stream is capable of seeking.
*/
private void skipBytes(InputStream in, int n) throws IOException {
while (n > 0) {
int len = in.read(tmpbuf, 0, n < tmpbuf.length ? n : tmpbuf.length);
if (len == -1) {
throw new EOFException();
}
n -= len;
}
}
}