blob: 724743bba6c878993e6ed9194028f067d5a15b11 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package java.util.zip;
27
28import java.io.SequenceInputStream;
29import java.io.ByteArrayInputStream;
30import java.io.InputStream;
31import java.io.IOException;
32import java.io.EOFException;
33
34/**
35 * This class implements a stream filter for reading compressed data in
36 * the GZIP file format.
37 *
38 * @see InflaterInputStream
39 * @author David Connelly
40 *
41 */
42public
43class GZIPInputStream extends InflaterInputStream {
44 /**
45 * CRC-32 for uncompressed data.
46 */
47 protected CRC32 crc = new CRC32();
48
49 /**
50 * Indicates end of input stream.
51 */
52 protected boolean eos;
53
54 private boolean closed = false;
55
56 /**
57 * Check to make sure that this stream has not been closed
58 */
59 private void ensureOpen() throws IOException {
60 if (closed) {
61 throw new IOException("Stream closed");
62 }
63 }
64
65 /**
66 * Creates a new input stream with the specified buffer size.
67 * @param in the input stream
68 * @param size the input buffer size
69 * @exception IOException if an I/O error has occurred
70 * @exception IllegalArgumentException if size is <= 0
71 */
72 public GZIPInputStream(InputStream in, int size) throws IOException {
73 super(in, new Inflater(true), size);
74 usesDefaultInflater = true;
75 readHeader();
76 crc.reset();
77 }
78
79 /**
80 * Creates a new input stream with a default buffer size.
81 * @param in the input stream
82 * @exception IOException if an I/O error has occurred
83 */
84 public GZIPInputStream(InputStream in) throws IOException {
85 this(in, 512);
86 }
87
88 /**
89 * Reads uncompressed data into an array of bytes. If <code>len</code> is not
90 * zero, the method will block until some input can be decompressed; otherwise,
91 * no bytes are read and <code>0</code> is returned.
92 * @param buf the buffer into which the data is read
93 * @param off the start offset in the destination array <code>b</code>
94 * @param len the maximum number of bytes read
95 * @return the actual number of bytes read, or -1 if the end of the
96 * compressed input stream is reached
97 * @exception NullPointerException If <code>buf</code> is <code>null</code>.
98 * @exception IndexOutOfBoundsException If <code>off</code> is negative,
99 * <code>len</code> is negative, or <code>len</code> is greater than
100 * <code>buf.length - off</code>
101 * @exception IOException if an I/O error has occurred or the compressed
102 * input data is corrupt
103 */
104 public int read(byte[] buf, int off, int len) throws IOException {
105 ensureOpen();
106 if (eos) {
107 return -1;
108 }
109 len = super.read(buf, off, len);
110 if (len == -1) {
111 readTrailer();
112 eos = true;
113 } else {
114 crc.update(buf, off, len);
115 }
116 return len;
117 }
118
119 /**
120 * Closes this input stream and releases any system resources associated
121 * with the stream.
122 * @exception IOException if an I/O error has occurred
123 */
124 public void close() throws IOException {
125 if (!closed) {
126 super.close();
127 eos = true;
128 closed = true;
129 }
130 }
131
132 /**
133 * GZIP header magic number.
134 */
135 public final static int GZIP_MAGIC = 0x8b1f;
136
137 /*
138 * File header flags.
139 */
140 private final static int FTEXT = 1; // Extra text
141 private final static int FHCRC = 2; // Header CRC
142 private final static int FEXTRA = 4; // Extra field
143 private final static int FNAME = 8; // File name
144 private final static int FCOMMENT = 16; // File comment
145
146 /*
147 * Reads GZIP member header.
148 */
149 private void readHeader() throws IOException {
150 CheckedInputStream in = new CheckedInputStream(this.in, crc);
151 crc.reset();
152 // Check header magic
153 if (readUShort(in) != GZIP_MAGIC) {
154 throw new IOException("Not in GZIP format");
155 }
156 // Check compression method
157 if (readUByte(in) != 8) {
158 throw new IOException("Unsupported compression method");
159 }
160 // Read flags
161 int flg = readUByte(in);
162 // Skip MTIME, XFL, and OS fields
163 skipBytes(in, 6);
164 // Skip optional extra field
165 if ((flg & FEXTRA) == FEXTRA) {
166 skipBytes(in, readUShort(in));
167 }
168 // Skip optional file name
169 if ((flg & FNAME) == FNAME) {
170 while (readUByte(in) != 0) ;
171 }
172 // Skip optional file comment
173 if ((flg & FCOMMENT) == FCOMMENT) {
174 while (readUByte(in) != 0) ;
175 }
176 // Check optional header CRC
177 if ((flg & FHCRC) == FHCRC) {
178 int v = (int)crc.getValue() & 0xffff;
179 if (readUShort(in) != v) {
180 throw new IOException("Corrupt GZIP header");
181 }
182 }
183 }
184
185 /*
186 * Reads GZIP member trailer.
187 */
188 private void readTrailer() throws IOException {
189 InputStream in = this.in;
190 int n = inf.getRemaining();
191 if (n > 0) {
192 in = new SequenceInputStream(
193 new ByteArrayInputStream(buf, len - n, n), in);
194 }
195 // Uses left-to-right evaluation order
196 if ((readUInt(in) != crc.getValue()) ||
197 // rfc1952; ISIZE is the input size modulo 2^32
198 (readUInt(in) != (inf.getBytesWritten() & 0xffffffffL)))
199 throw new IOException("Corrupt GZIP trailer");
200 }
201
202 /*
203 * Reads unsigned integer in Intel byte order.
204 */
205 private long readUInt(InputStream in) throws IOException {
206 long s = readUShort(in);
207 return ((long)readUShort(in) << 16) | s;
208 }
209
210 /*
211 * Reads unsigned short in Intel byte order.
212 */
213 private int readUShort(InputStream in) throws IOException {
214 int b = readUByte(in);
215 return ((int)readUByte(in) << 8) | b;
216 }
217
218 /*
219 * Reads unsigned byte.
220 */
221 private int readUByte(InputStream in) throws IOException {
222 int b = in.read();
223 if (b == -1) {
224 throw new EOFException();
225 }
226 if (b < -1 || b > 255) {
227 // Report on this.in, not argument in; see read{Header, Trailer}.
228 throw new IOException(this.in.getClass().getName()
229 + ".read() returned value out of range -1..255: " + b);
230 }
231 return b;
232 }
233
234
235 private byte[] tmpbuf = new byte[128];
236
237 /*
238 * Skips bytes of input data blocking until all bytes are skipped.
239 * Does not assume that the input stream is capable of seeking.
240 */
241 private void skipBytes(InputStream in, int n) throws IOException {
242 while (n > 0) {
243 int len = in.read(tmpbuf, 0, n < tmpbuf.length ? n : tmpbuf.length);
244 if (len == -1) {
245 throw new EOFException();
246 }
247 n -= len;
248 }
249 }
250}