InflaterInputStream.java revision cec4dd4b1d33f78997603d0f89c0d0e56e64dbcd
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
18package java.util.zip;
19
20import java.io.EOFException;
21import java.io.FilterInputStream;
22import java.io.IOException;
23import java.io.InputStream;
24
25import org.apache.harmony.archive.internal.nls.Messages;
26
27/**
28 * This class provides an implementation of {@code FilterInputStream} that
29 * uncompresses data that was compressed using the <i>DEFLATE</i> algorithm
30 * (see <a href="http://www.gzip.org/algorithm.txt">specification</a>).
31 * Basically it wraps the {@code Inflater} class and takes care of the
32 * buffering.
33 *
34 * @see Inflater
35 * @see DeflaterOutputStream
36 */
37public class InflaterInputStream extends FilterInputStream {
38
39    /**
40     * The inflater used for this stream.
41     */
42    protected Inflater inf;
43
44    /**
45     * The input buffer used for decompression.
46     */
47    protected byte[] buf;
48
49    /**
50     * The length of the buffer.
51     */
52    protected int len;
53
54    boolean closed;
55
56    /**
57     * True if this stream's last byte has been returned to the user. This
58     * could be because the underlying stream has been exhausted, or if errors
59     * were encountered while inflating that stream.
60     */
61    boolean eof;
62
63    static final int BUF_SIZE = 512;
64
65    int nativeEndBufSize = 0; // android-only
66
67    /**
68     * This is the most basic constructor. You only need to pass the {@code
69     * InputStream} from which the compressed data is to be read from. Default
70     * settings for the {@code Inflater} and internal buffer are be used. In
71     * particular the Inflater expects a ZLIB header from the input stream.
72     *
73     * @param is
74     *            the {@code InputStream} to read data from.
75     */
76    public InflaterInputStream(InputStream is) {
77        this(is, new Inflater(), BUF_SIZE);
78    }
79
80    /**
81     * This constructor lets you pass a specifically initialized Inflater,
82     * for example one that expects no ZLIB header.
83     *
84     * @param is
85     *            the {@code InputStream} to read data from.
86     * @param inf
87     *            the specific {@code Inflater} for uncompressing data.
88     */
89    public InflaterInputStream(InputStream is, Inflater inf) {
90        this(is, inf, BUF_SIZE);
91    }
92
93    /**
94     * This constructor lets you specify both the {@code Inflater} as well as
95     * the internal buffer size to be used.
96     *
97     * @param is
98     *            the {@code InputStream} to read data from.
99     * @param inf
100     *            the specific {@code Inflater} for uncompressing data.
101     * @param bsize
102     *            the size to be used for the internal buffer.
103     */
104    public InflaterInputStream(InputStream is, Inflater inf, int bsize) {
105        super(is);
106        if (is == null || inf == null) {
107            throw new NullPointerException();
108        }
109        if (bsize <= 0) {
110            throw new IllegalArgumentException();
111        }
112        this.inf = inf;
113        // BEGIN android-only
114        if (is instanceof ZipFile.RAFStream) {
115            nativeEndBufSize = bsize;
116        } else {
117            buf = new byte[bsize];
118        }
119        // END android-only
120    }
121
122    /**
123     * Reads a single byte of decompressed data.
124     *
125     * @return the byte read.
126     * @throws IOException
127     *             if an error occurs reading the byte.
128     */
129    @Override
130    public int read() throws IOException {
131        byte[] b = new byte[1];
132        if (read(b, 0, 1) == -1) {
133            return -1;
134        }
135        return b[0] & 0xff;
136    }
137
138    /**
139     * Reads up to {@code nbytes} of decompressed data and stores it in
140     * {@code buffer} starting at {@code off}.
141     *
142     * @param buffer
143     *            the buffer to write data to.
144     * @param off
145     *            offset in buffer to start writing.
146     * @param nbytes
147     *            number of bytes to read.
148     * @return Number of uncompressed bytes read
149     * @throws IOException
150     *             if an IOException occurs.
151     */
152    @Override
153    public int read(byte[] buffer, int off, int nbytes) throws IOException {
154        checkClosed();
155        if (buffer == null) {
156            throw new NullPointerException();
157        }
158
159        if (off < 0 || nbytes < 0 || off + nbytes > buffer.length) {
160            throw new IndexOutOfBoundsException();
161        }
162
163        if (nbytes == 0) {
164            return 0;
165        }
166
167        if (eof) {
168            return -1;
169        }
170
171        // avoid int overflow, check null buffer
172        if (off > buffer.length || nbytes < 0 || off < 0
173                || buffer.length - off < nbytes) {
174            throw new ArrayIndexOutOfBoundsException();
175        }
176
177        do {
178            if (inf.needsInput()) {
179                fill();
180            }
181            // Invariant: if reading returns -1 or throws, eof must be true.
182            // It may also be true if the next read() should return -1.
183            try {
184                int result = inf.inflate(buffer, off, nbytes);
185                eof = inf.finished();
186                if (result > 0) {
187                    return result;
188                } else if (eof) {
189                    return -1;
190                } else if (inf.needsDictionary()) {
191                    eof = true;
192                    return -1;
193                } else if (len == -1) {
194                    eof = true;
195                    throw new EOFException();
196                    // If result == 0, fill() and try again
197                }
198            } catch (DataFormatException e) {
199                eof = true;
200                if (len == -1) {
201                    throw new EOFException();
202                }
203                throw (IOException) (new IOException().initCause(e));
204            }
205        } while (true);
206    }
207
208    /**
209     * Fills the input buffer with data to be decompressed.
210     *
211     * @throws IOException
212     *             if an {@code IOException} occurs.
213     */
214    protected void fill() throws IOException {
215        checkClosed();
216        // BEGIN android-only
217        if (nativeEndBufSize > 0) {
218            ZipFile.RAFStream is = (ZipFile.RAFStream)in;
219            synchronized (is.mSharedRaf) {
220                long len = is.mLength - is.mOffset;
221                if (len > nativeEndBufSize) len = nativeEndBufSize;
222                int cnt = inf.setFileInput(is.mSharedRaf.getFD(), is.mOffset, (int)nativeEndBufSize);
223                is.skip(cnt);
224            }
225        } else {
226            if ((len = in.read(buf)) > 0) {
227                inf.setInput(buf, 0, len);
228            }
229        }
230        // END android-only
231    }
232
233    /**
234     * Skips up to n bytes of uncompressed data.
235     *
236     * @param nbytes
237     *            the number of bytes to skip.
238     * @return the number of uncompressed bytes skipped.
239     * @throws IOException
240     *             if an error occurs skipping.
241     */
242    @Override
243    public long skip(long nbytes) throws IOException {
244        if (nbytes >= 0) {
245            if (buf == null) {
246                buf = new byte[(int)Math.min(nbytes, BUF_SIZE)];
247            }
248            long count = 0, rem = 0;
249            while (count < nbytes) {
250                int x = read(buf, 0,
251                        (rem = nbytes - count) > buf.length ? buf.length
252                                : (int) rem);
253                if (x == -1) {
254                    return count;
255                }
256                count += x;
257            }
258            return count;
259        }
260        throw new IllegalArgumentException();
261    }
262
263    /**
264     * Returns 0 when when this stream has exhausted its input; and 1 otherwise.
265     * A result of 1 does not guarantee that further bytes can be returned,
266     * with or without blocking.
267     *
268     * <p>Although consistent with the RI, this behavior is inconsistent with
269     * {@link InputStream#available()}, and violates the <a
270     * href="http://en.wikipedia.org/wiki/Liskov_substitution_principle">Liskov
271     * Substitution Principle</a>. This method should not be used.
272     *
273     * @return 0 if no further bytes are available. Otherwise returns 1,
274     *         which suggests (but does not guarantee) that additional bytes are
275     *         available.
276     * @throws IOException if this stream is closed or an error occurs
277     */
278    @Override
279    public int available() throws IOException {
280        checkClosed();
281        if (eof) {
282            return 0;
283        }
284        return 1;
285    }
286
287    /**
288     * Closes the input stream.
289     *
290     * @throws IOException
291     *             If an error occurs closing the input stream.
292     */
293    @Override
294    public void close() throws IOException {
295        if (!closed) {
296            inf.end();
297            closed = true;
298            eof = true;
299            super.close();
300        }
301    }
302
303    /**
304     * Marks the current position in the stream. This implementation overrides
305     * the super type implementation to do nothing at all.
306     *
307     * @param readlimit
308     *            of no use.
309     */
310    @Override
311    public void mark(int readlimit) {
312        // do nothing
313    }
314
315    /**
316     * Reset the position of the stream to the last marked position. This
317     * implementation overrides the supertype implementation and always throws
318     * an {@link IOException IOException} when called.
319     *
320     * @throws IOException
321     *             if the method is called
322     */
323    @Override
324    public void reset() throws IOException {
325        throw new IOException();
326    }
327
328    /**
329     * Returns whether the receiver implements {@code mark} semantics. This type
330     * does not support {@code mark()}, so always responds {@code false}.
331     *
332     * @return false, always
333     */
334    @Override
335    public boolean markSupported() {
336        return false;
337    }
338
339    private void checkClosed() throws IOException {
340        if (closed) {
341            throw new IOException("Stream is closed");
342        }
343    }
344}
345