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