289 lines · 9.4 KB · Java
RawDownload
/* * 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;        }    }}