OSDN Git Service

bb84f5b373fd51d4134bdf5a8ce5b66b3943bbca
[android-x86/dalvik.git] / libcore / archive / src / main / java / java / util / zip / GZIPInputStream.java
1 /* 
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  * 
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  * 
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 package java.util.zip;
19
20 import java.io.EOFException;
21 import java.io.IOException;
22 import java.io.InputStream;
23
24 import org.apache.harmony.archive.internal.nls.Messages;
25
26 /**
27  * The {@code GZIPInputStream} class is used to read data stored in the GZIP
28  * format, reading and decompressing GZIP data from the underlying stream into
29  * its buffer.
30  */
31 public class GZIPInputStream extends InflaterInputStream {
32
33     private static final int FCOMMENT = 16;
34
35     private static final int FEXTRA = 4;
36
37     private static final int FHCRC = 2;
38
39     private static final int FNAME = 8;
40
41     /**
42      * The magic header for the GZIP format.
43      */
44     public final static int GZIP_MAGIC = 0x8b1f;
45
46     /**
47      * The checksum algorithm used when handling uncompressed data.
48      */
49     protected CRC32 crc = new CRC32();
50
51     /**
52      * Indicates the end of the input stream.
53      */
54     protected boolean eos = false;
55
56     /**
57      * Construct a {@code GZIPInputStream} to read from GZIP data from the
58      * underlying stream.
59      *
60      * @param is
61      *            the {@code InputStream} to read data from.
62      * @throws IOException
63      *             if an {@code IOException} occurs.
64      */
65     public GZIPInputStream(InputStream is) throws IOException {
66         this(is, BUF_SIZE);
67     }
68
69     /**
70      * Construct a {@code GZIPInputStream} to read from GZIP data from the
71      * underlying stream. Set the internal buffer size to {@code size}.
72      * 
73      * @param is
74      *            the {@code InputStream} to read data from.
75      * @param size
76      *            the internal read buffer size.
77      * @throws IOException
78      *             if an {@code IOException} occurs.
79      */
80     public GZIPInputStream(InputStream is, int size) throws IOException {
81         super(is, new Inflater(true), size);
82         byte[] header = new byte[10];
83         readFully(header, 0, header.length);
84         if (getShort(header, 0) != GZIP_MAGIC) {
85             throw new IOException(Messages.getString("archive.1F")); //$NON-NLS-1$;
86         }
87         int flags = header[3];
88         boolean hcrc = (flags & FHCRC) != 0;
89         if (hcrc) {
90             crc.update(header, 0, header.length);
91         }
92         if ((flags & FEXTRA) != 0) {
93             readFully(header, 0, 2);
94             if (hcrc) {
95                 crc.update(header, 0, 2);
96             }
97             int length = getShort(header, 0);
98             while (length > 0) {
99                 int max = length > buf.length ? buf.length : length;
100                 int result = in.read(buf, 0, max);
101                 if (result == -1) {
102                     throw new EOFException();
103                 }
104                 if (hcrc) {
105                     crc.update(buf, 0, result);
106                 }
107                 length -= result;
108             }
109         }
110         if ((flags & FNAME) != 0) {
111             readZeroTerminated(hcrc);
112         }
113         if ((flags & FCOMMENT) != 0) {
114             readZeroTerminated(hcrc);
115         }
116         if (hcrc) {
117             readFully(header, 0, 2);
118             int crc16 = getShort(header, 0);
119             if ((crc.getValue() & 0xffff) != crc16) {
120                 throw new IOException(Messages.getString("archive.20")); //$NON-NLS-1$
121             }
122             crc.reset();
123         }
124     }
125
126     /**
127      * Closes this stream and any underlying streams.
128      */
129     @Override
130     public void close() throws IOException {
131         eos = true;
132         super.close();
133     }
134
135     private long getLong(byte[] buffer, int off) {
136         long l = 0;
137         l |= (buffer[off] & 0xFF);
138         l |= (buffer[off + 1] & 0xFF) << 8;
139         l |= (buffer[off + 2] & 0xFF) << 16;
140         l |= ((long) (buffer[off + 3] & 0xFF)) << 24;
141         return l;
142     }
143
144     private int getShort(byte[] buffer, int off) {
145         return (buffer[off] & 0xFF) | ((buffer[off + 1] & 0xFF) << 8);
146     }
147
148     /**
149      * Reads and decompresses GZIP data from the underlying stream into the
150      * given buffer.
151      *
152      * @param buffer
153      *            Buffer to receive data
154      * @param off
155      *            Offset in buffer to store data
156      * @param nbytes
157      *            Number of bytes to read
158      */
159     @Override
160     public int read(byte[] buffer, int off, int nbytes) throws IOException {
161         if (closed) {
162             throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
163         }
164         if (eof) {
165             return -1;
166         }
167         // avoid int overflow, check null buffer
168         if (off <= buffer.length && nbytes >= 0 && off >= 0
169                 && buffer.length - off >= nbytes) {
170             int val = super.read(buffer, off, nbytes);
171             if (val != -1) {
172                 crc.update(buffer, off, val);
173             } else if (!eos) {
174                 eos = true;
175                 // Get non-compressed bytes read by fill
176                 int size = inf.getRemaining();
177                 final int trailerSize = 8; // crc (4 bytes) + total out (4
178                 // bytes)
179                 byte[] b = new byte[trailerSize];
180                 int copySize = (size > trailerSize) ? trailerSize : size;
181
182                 System.arraycopy(buf, len - size, b, 0, copySize);
183                 readFully(b, copySize, trailerSize - copySize);
184
185                 if (getLong(b, 0) != crc.getValue()) {
186                     throw new IOException(Messages.getString("archive.20")); //$NON-NLS-1$
187                 }
188                 if ((int) getLong(b, 4) != inf.getTotalOut()) {
189                     throw new IOException(Messages.getString("archive.21")); //$NON-NLS-1$
190                 }
191             }
192             return val;
193         }
194         throw new ArrayIndexOutOfBoundsException();
195     }
196
197     private void readFully(byte[] buffer, int offset, int length)
198             throws IOException {
199         int result;
200         while (length > 0) {
201             result = in.read(buffer, offset, length);
202             if (result == -1) {
203                 throw new EOFException();
204             }
205             offset += result;
206             length -= result;
207         }
208     }
209
210     private void readZeroTerminated(boolean hcrc) throws IOException {
211         int result;
212         while ((result = in.read()) > 0) {
213             if (hcrc) {
214                 crc.update(result);
215             }
216         }
217         if (result == -1) {
218             throw new EOFException();
219         }
220         // Add the zero
221         if (hcrc) {
222             crc.update(result);
223         }
224     }
225 }