1 /*
2  * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
3  * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
4  *
5  *
6  *
7  *
8  *
9  *
10  *
11  *
12  *
13  *
14  *
15  *
16  *
17  *
18  *
19  *
20  *
21  *
22  *
23  *
24  */

25
26 package java.util.zip;
27
28 import java.io.SequenceInputStream;
29 import java.io.ByteArrayInputStream;
30 import java.io.FilterInputStream;
31 import java.io.InputStream;
32 import java.io.IOException;
33 import java.io.EOFException;
34
35 /**
36  * This class implements a stream filter for reading compressed data in
37  * the GZIP file format.
38  *
39  * @see         InflaterInputStream
40  * @author      David Connelly
41  *
42  */

43 public
44 class GZIPInputStream extends InflaterInputStream {
45     /**
46      * CRC-32 for uncompressed data.
47      */

48     protected CRC32 crc = new CRC32();
49
50     /**
51      * Indicates end of input stream.
52      */

53     protected boolean eos;
54
55     private boolean closed = false;
56
57     /**
58      * Check to make sure that this stream has not been closed
59      */

60     private void ensureOpen() throws IOException {
61         if (closed) {
62             throw new IOException("Stream closed");
63         }
64     }
65
66     /**
67      * Creates a new input stream with the specified buffer size.
68      * @param in the input stream
69      * @param size the input buffer size
70      *
71      * @exception ZipException if a GZIP format error has occurred or the
72      *                         compression method used is unsupported
73      * @exception IOException if an I/O error has occurred
74      * @exception IllegalArgumentException if {@code size <= 0}
75      */

76     public GZIPInputStream(InputStream in, int size) throws IOException {
77         super(in, new Inflater(true), size);
78         usesDefaultInflater = true;
79         readHeader(in);
80     }
81
82     /**
83      * Creates a new input stream with a default buffer size.
84      * @param in the input stream
85      *
86      * @exception ZipException if a GZIP format error has occurred or the
87      *                         compression method used is unsupported
88      * @exception IOException if an I/O error has occurred
89      */

90     public GZIPInputStream(InputStream in) throws IOException {
91         this(in, 512);
92     }
93
94     /**
95      * Reads uncompressed data into an array of bytes. If <code>len</code> is not
96      * zero, the method will block until some input can be decompressed; otherwise,
97      * no bytes are read and <code>0</code> is returned.
98      * @param buf the buffer into which the data is read
99      * @param off the start offset in the destination array <code>b</code>
100      * @param len the maximum number of bytes read
101      * @return  the actual number of bytes read, or -1 if the end of the
102      *          compressed input stream is reached
103      *
104      * @exception  NullPointerException If <code>buf</code> is <code>null</code>.
105      * @exception  IndexOutOfBoundsException If <code>off</code> is negative,
106      * <code>len</code> is negative, or <code>len</code> is greater than
107      * <code>buf.length - off</code>
108      * @exception ZipException if the compressed input data is corrupt.
109      * @exception IOException if an I/O error has occurred.
110      *
111      */

112     public int read(byte[] buf, int off, int len) throws IOException {
113         ensureOpen();
114         if (eos) {
115             return -1;
116         }
117         int n = super.read(buf, off, len);
118         if (n == -1) {
119             if (readTrailer())
120                 eos = true;
121             else
122                 return this.read(buf, off, len);
123         } else {
124             crc.update(buf, off, n);
125         }
126         return n;
127     }
128
129     /**
130      * Closes this input stream and releases any system resources associated
131      * with the stream.
132      * @exception IOException if an I/O error has occurred
133      */

134     public void close() throws IOException {
135         if (!closed) {
136             super.close();
137             eos = true;
138             closed = true;
139         }
140     }
141
142     /**
143      * GZIP header magic number.
144      */

145     public final static int GZIP_MAGIC = 0x8b1f;
146
147     /*
148      * File header flags.
149      */

150     private final static int FTEXT      = 1;    // Extra text
151     private final static int FHCRC      = 2;    // Header CRC
152     private final static int FEXTRA     = 4;    // Extra field
153     private final static int FNAME      = 8;    // File name
154     private final static int FCOMMENT   = 16;   // File comment
155
156     /*
157      * Reads GZIP member header and returns the total byte number
158      * of this member header.
159      */

160     private int readHeader(InputStream this_in) throws IOException {
161         CheckedInputStream in = new CheckedInputStream(this_in, crc);
162         crc.reset();
163         // Check header magic
164         if (readUShort(in) != GZIP_MAGIC) {
165             throw new ZipException("Not in GZIP format");
166         }
167         // Check compression method
168         if (readUByte(in) != 8) {
169             throw new ZipException("Unsupported compression method");
170         }
171         // Read flags
172         int flg = readUByte(in);
173         // Skip MTIME, XFL, and OS fields
174         skipBytes(in, 6);
175         int n = 2 + 2 + 6;
176         // Skip optional extra field
177         if ((flg & FEXTRA) == FEXTRA) {
178             int m = readUShort(in);
179             skipBytes(in, m);
180             n += m + 2;
181         }
182         // Skip optional file name
183         if ((flg & FNAME) == FNAME) {
184             do {
185                 n++;
186             } while (readUByte(in) != 0);
187         }
188         // Skip optional file comment
189         if ((flg & FCOMMENT) == FCOMMENT) {
190             do {
191                 n++;
192             } while (readUByte(in) != 0);
193         }
194         // Check optional header CRC
195         if ((flg & FHCRC) == FHCRC) {
196             int v = (int)crc.getValue() & 0xffff;
197             if (readUShort(in) != v) {
198                 throw new ZipException("Corrupt GZIP header");
199             }
200             n += 2;
201         }
202         crc.reset();
203         return n;
204     }
205
206     /*
207      * Reads GZIP member trailer and returns true if the eos
208      * reached, false if there are more (concatenated gzip
209      * data set)
210      */

211     private boolean readTrailer() throws IOException {
212         InputStream in = this.in;
213         int n = inf.getRemaining();
214         if (n > 0) {
215             in = new SequenceInputStream(
216                         new ByteArrayInputStream(buf, len - n, n),
217                         new FilterInputStream(in) {
218                             public void close() throws IOException {}
219                         });
220         }
221         // Uses left-to-right evaluation order
222         if ((readUInt(in) != crc.getValue()) ||
223             // rfc1952; ISIZE is the input size modulo 2^32
224             (readUInt(in) != (inf.getBytesWritten() & 0xffffffffL)))
225             throw new ZipException("Corrupt GZIP trailer");
226
227         // If there are more bytes available in "in" or
228         // the leftover in the "inf" is > 26 bytes:
229         // this.trailer(8) + next.header.min(10) + next.trailer(8)
230         // try concatenated case
231         if (this.in.available() > 0 || n > 26) {
232             int m = 8;                  // this.trailer
233             try {
234                 m += readHeader(in);    // next.header
235             } catch (IOException ze) {
236                 return true;  // ignore any malformed, do nothing
237             }
238             inf.reset();
239             if (n > m)
240                 inf.setInput(buf, len - n + m, n - m);
241             return false;
242         }
243         return true;
244     }
245
246     /*
247      * Reads unsigned integer in Intel byte order.
248      */

249     private long readUInt(InputStream in) throws IOException {
250         long s = readUShort(in);
251         return ((long)readUShort(in) << 16) | s;
252     }
253
254     /*
255      * Reads unsigned short in Intel byte order.
256      */

257     private int readUShort(InputStream in) throws IOException {
258         int b = readUByte(in);
259         return (readUByte(in) << 8) | b;
260     }
261
262     /*
263      * Reads unsigned byte.
264      */

265     private int readUByte(InputStream in) throws IOException {
266         int b = in.read();
267         if (b == -1) {
268             throw new EOFException();
269         }
270         if (b < -1 || b > 255) {
271             // Report on this.in, not argument in; see read{Header, Trailer}.
272             throw new IOException(this.in.getClass().getName()
273                 + ".read() returned value out of range -1..255: " + b);
274         }
275         return b;
276     }
277
278     private byte[] tmpbuf = new byte[128];
279
280     /*
281      * Skips bytes of input data blocking until all bytes are skipped.
282      * Does not assume that the input stream is capable of seeking.
283      */

284     private void skipBytes(InputStream in, int n) throws IOException {
285         while (n > 0) {
286             int len = in.read(tmpbuf, 0, n < tmpbuf.length ? n : tmpbuf.length);
287             if (len == -1) {
288                 throw new EOFException();
289             }
290             n -= len;
291         }
292     }
293 }
294
Powered by JavaMelody