001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.util.zip;
019:
020: import java.io.EOFException;
021: import java.io.IOException;
022: import java.io.InputStream;
023:
024: import org.apache.harmony.archive.internal.nls.Messages;
025:
026: /**
027: * The GZIPInputStream class is used to read data stored in the GZIP format.
028: */
029: public class GZIPInputStream extends InflaterInputStream {
030:
031: private static final int FCOMMENT = 16;
032:
033: private static final int FEXTRA = 4;
034:
035: private static final int FHCRC = 2;
036:
037: private static final int FNAME = 8;
038:
039: /** Value of GZIP header magic number. */
040: public final static int GZIP_MAGIC = 0x8b1f;
041:
042: protected CRC32 crc = new CRC32();
043:
044: protected boolean eos = false;
045:
046: /**
047: * Construct a GZIPInputStream to read from GZIP data from the underlying
048: * stream
049: *
050: * @param is
051: * InputStream to read data from
052: * @throws IOException
053: * if an IO error occurs reading the stream
054: */
055: public GZIPInputStream(InputStream is) throws IOException {
056: this (is, BUF_SIZE);
057: }
058:
059: /**
060: * Construct a GZIPInputStream to read from GZIP data from the underlying
061: * stream. Set the internal buffer size to size
062: *
063: * @param is
064: * InputStream to read data from
065: * @param size
066: * Internal read buffer size
067: * @throws IOException
068: * if an IO exception occurs reading the stream
069: */
070: public GZIPInputStream(InputStream is, int size) throws IOException {
071: super (is, new Inflater(true), size);
072: byte[] header = new byte[10];
073: readFully(header, 0, header.length);
074: if (getShort(header, 0) != GZIP_MAGIC) {
075: throw new IOException(Messages.getString("archive.1F")); //$NON-NLS-1$;
076: }
077: int flags = header[3];
078: boolean hcrc = (flags & FHCRC) != 0;
079: if (hcrc) {
080: crc.update(header, 0, header.length);
081: }
082: if ((flags & FEXTRA) != 0) {
083: readFully(header, 0, 2);
084: if (hcrc) {
085: crc.update(header, 0, 2);
086: }
087: int length = getShort(header, 0);
088: while (length > 0) {
089: int max = length > buf.length ? buf.length : length;
090: int result = in.read(buf, 0, max);
091: if (result == -1) {
092: throw new EOFException();
093: }
094: if (hcrc) {
095: crc.update(buf, 0, result);
096: }
097: length -= result;
098: }
099: }
100: if ((flags & FNAME) != 0) {
101: readZeroTerminated(hcrc);
102: }
103: if ((flags & FCOMMENT) != 0) {
104: readZeroTerminated(hcrc);
105: }
106: if (hcrc) {
107: readFully(header, 0, 2);
108: int crc16 = getShort(header, 0);
109: if ((crc.getValue() & 0xffff) != crc16) {
110: throw new IOException(Messages.getString("archive.20")); //$NON-NLS-1$
111: }
112: crc.reset();
113: }
114: }
115:
116: /**
117: * Closes this stream and any underlying streams.
118: */
119: @Override
120: public void close() throws IOException {
121: eos = true;
122: super .close();
123: }
124:
125: private long getLong(byte[] buffer, int off) {
126: long l = 0;
127: l |= (buffer[off] & 0xFF);
128: l |= (buffer[off + 1] & 0xFF) << 8;
129: l |= (buffer[off + 2] & 0xFF) << 16;
130: l |= ((long) (buffer[off + 3] & 0xFF)) << 24;
131: return l;
132: }
133:
134: private int getShort(byte[] buffer, int off) {
135: return (buffer[off] & 0xFF) | ((buffer[off + 1] & 0xFF) << 8);
136: }
137:
138: /**
139: * Reads and decompresses GZIP data from the underlying stream into the
140: * given buffer.
141: *
142: * @param buffer
143: * Buffer to receive data
144: * @param off
145: * Offset in buffer to store data
146: * @param nbytes
147: * Number of bytes to read
148: */
149: @Override
150: public int read(byte[] buffer, int off, int nbytes)
151: throws IOException {
152: if (closed) {
153: throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
154: }
155: if (eof) {
156: return -1;
157: }
158: // avoid int overflow, check null buffer
159: if (off <= buffer.length && nbytes >= 0 && off >= 0
160: && buffer.length - off >= nbytes) {
161: int val = super .read(buffer, off, nbytes);
162: if (val != -1) {
163: crc.update(buffer, off, val);
164: } else if (!eos) {
165: eos = true;
166: // Get non-compressed bytes read by fill
167: int size = inf.getRemaining();
168: final int trailerSize = 8; // crc (4 bytes) + total out (4
169: // bytes)
170: byte[] b = new byte[trailerSize];
171: int copySize = (size > trailerSize) ? trailerSize
172: : size;
173:
174: System.arraycopy(buf, len - size, b, 0, copySize);
175: readFully(b, copySize, trailerSize - copySize);
176:
177: if (getLong(b, 0) != crc.getValue()) {
178: throw new IOException(Messages
179: .getString("archive.20")); //$NON-NLS-1$
180: }
181: if ((int) getLong(b, 4) != inf.getTotalOut()) {
182: throw new IOException(Messages
183: .getString("archive.21")); //$NON-NLS-1$
184: }
185: }
186: return val;
187: }
188: throw new ArrayIndexOutOfBoundsException();
189: }
190:
191: private void readFully(byte[] buffer, int offset, int length)
192: throws IOException {
193: int result;
194: while (length > 0) {
195: result = in.read(buffer, offset, length);
196: if (result == -1) {
197: throw new EOFException();
198: }
199: offset += result;
200: length -= result;
201: }
202: }
203:
204: private void readZeroTerminated(boolean hcrc) throws IOException {
205: int result;
206: while ((result = in.read()) > 0) {
207: if (hcrc) {
208: crc.update(result);
209: }
210: }
211: if (result == -1) {
212: throw new EOFException();
213: }
214: // Add the zero
215: if (hcrc) {
216: crc.update(result);
217: }
218: }
219: }
|