001: /*
002: * @(#)GZIPInputStream.java 1.30 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package java.util.zip;
029:
030: import java.io.SequenceInputStream;
031: import java.io.ByteArrayInputStream;
032: import java.io.InputStream;
033: import java.io.IOException;
034: import java.io.EOFException;
035:
036: /**
037: * This class implements a stream filter for reading compressed data in
038: * the GZIP format.
039: *
040: * @see InflaterInputStream
041: * @version 1.22, 02/02/00
042: * @author David Connelly
043: *
044: */
045: public class GZIPInputStream extends InflaterInputStream {
046: /**
047: * CRC-32 for uncompressed data.
048: */
049: protected CRC32 crc = new CRC32();
050:
051: /**
052: * Indicates end of input stream.
053: */
054: protected boolean eos;
055:
056: private boolean closed = false;
057:
058: /**
059: * Check to make sure that this stream has not been closed
060: */
061: private void ensureOpen() throws IOException {
062: if (closed) {
063: throw new IOException("Stream closed");
064: }
065: }
066:
067: /**
068: * Creates a new input stream with the specified buffer size.
069: * @param in the input stream
070: * @param size the input buffer size
071: * @exception IOException if an I/O error has occurred
072: * @exception IllegalArgumentException if size is <= 0
073: */
074: public GZIPInputStream(InputStream in, int size) throws IOException {
075: super (in, new Inflater(true), size);
076: usesDefaultInflater = true;
077: readHeader();
078: crc.reset();
079: }
080:
081: /**
082: * Creates a new input stream with a default buffer size.
083: * @param in the input stream
084: * @exception IOException if an I/O error has occurred
085: */
086: public GZIPInputStream(InputStream in) throws IOException {
087: this (in, 512);
088: }
089:
090: /**
091: * Reads uncompressed data into an array of bytes. Blocks until enough
092: * input is available for decompression.
093: * @param buf the buffer into which the data is read
094: * @param off the start offset of the data
095: * @param len the maximum number of bytes read
096: * @return the actual number of bytes read, or -1 if the end of the
097: * compressed input stream is reached
098: * @exception IOException if an I/O error has occurred or the compressed
099: * input data is corrupt
100: */
101: public int read(byte[] buf, int off, int len) throws IOException {
102: ensureOpen();
103: if (eos) {
104: return -1;
105: }
106: len = super .read(buf, off, len);
107: if (len == -1) {
108: readTrailer();
109: eos = true;
110: } else {
111: crc.update(buf, off, len);
112: }
113: return len;
114: }
115:
116: /**
117: * Closes the input stream.
118: * @exception IOException if an I/O error has occurred
119: */
120: public void close() throws IOException {
121: if (!closed) {
122: super .close();
123: eos = true;
124: closed = true;
125: }
126: }
127:
128: /**
129: * GZIP header magic number.
130: */
131: public final static int GZIP_MAGIC = 0x8b1f;
132:
133: /*
134: * File header flags.
135: */
136: private final static int FTEXT = 1; // Extra text
137: private final static int FHCRC = 2; // Header CRC
138: private final static int FEXTRA = 4; // Extra field
139: private final static int FNAME = 8; // File name
140: private final static int FCOMMENT = 16; // File comment
141:
142: /*
143: * Reads GZIP member header.
144: */
145: private void readHeader() throws IOException {
146: CheckedInputStream in = new CheckedInputStream(this .in, crc);
147: crc.reset();
148: // Check header magic
149: if (readUShort(in) != GZIP_MAGIC) {
150: throw new IOException("Not in GZIP format");
151: }
152: // Check compression method
153: if (readUByte(in) != 8) {
154: throw new IOException("Unsupported compression method");
155: }
156: // Read flags
157: int flg = readUByte(in);
158: // Skip MTIME, XFL, and OS fields
159: skipBytes(in, 6);
160: // Skip optional extra field
161: if ((flg & FEXTRA) == FEXTRA) {
162: skipBytes(in, readUShort(in));
163: }
164: // Skip optional file name
165: if ((flg & FNAME) == FNAME) {
166: while (readUByte(in) != 0)
167: ;
168: }
169: // Skip optional file comment
170: if ((flg & FCOMMENT) == FCOMMENT) {
171: while (readUByte(in) != 0)
172: ;
173: }
174: // Check optional header CRC
175: if ((flg & FHCRC) == FHCRC) {
176: int v = (int) crc.getValue() & 0xffff;
177: if (readUShort(in) != v) {
178: throw new IOException("Corrupt GZIP header");
179: }
180: }
181: }
182:
183: /*
184: * Reads GZIP member trailer.
185: */
186: private void readTrailer() throws IOException {
187: InputStream in = this .in;
188: int n = inf.getRemaining();
189: if (n > 0) {
190: in = new SequenceInputStream(new ByteArrayInputStream(buf,
191: len - n, n), in);
192: }
193: long v = crc.getValue();
194: if (readUInt(in) != v || readUInt(in) != inf.getTotalOut()) {
195: throw new IOException("Corrupt GZIP trailer");
196: }
197: }
198:
199: /*
200: * Reads unsigned integer in little-endian byte order.
201: */
202: private long readUInt(InputStream in) throws IOException {
203: long s = readUShort(in);
204: return ((long) readUShort(in) << 16) | s;
205: }
206:
207: /*
208: * Reads unsigned short in little-endian byte order.
209: */
210: private int readUShort(InputStream in) throws IOException {
211: int b = readUByte(in);
212: return ((int) readUByte(in) << 8) | b;
213: }
214:
215: /*
216: * Reads unsigned byte.
217: */
218: private int readUByte(InputStream in) throws IOException {
219: int b = in.read();
220: if (b == -1) {
221: throw new EOFException();
222: }
223: return b;
224: }
225:
226: private byte[] tmpbuf = new byte[128];
227:
228: /*
229: * Skips bytes of input data blocking until all bytes are skipped.
230: * Does not assume that the input stream is capable of seeking.
231: */
232: private void skipBytes(InputStream in, int n) throws IOException {
233: while (n > 0) {
234: int len = in.read(tmpbuf, 0, n < tmpbuf.length ? n
235: : tmpbuf.length);
236: if (len == -1) {
237: throw new EOFException();
238: }
239: n -= len;
240: }
241: }
242: }
|