InflaterInputStream.java revision f9480f317cddcec859025833b748f096247a40aa
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;
24import java.util.Arrays;
25import libcore.base.Streams;
26
27/**
28 * This class provides an implementation of {@code FilterInputStream} that
29 * decompresses 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 inflater
87     *            the specific {@code Inflater} for decompressing data.
88     */
89    public InflaterInputStream(InputStream is, Inflater inflater) {
90        this(is, inflater, 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 inflater
100     *            the specific {@code Inflater} for decompressing data.
101     * @param bsize
102     *            the size to be used for the internal buffer.
103     */
104    public InflaterInputStream(InputStream is, Inflater inflater, int bsize) {
105        super(is);
106        if (is == null || inflater == null) {
107            throw new NullPointerException();
108        }
109        if (bsize <= 0) {
110            throw new IllegalArgumentException();
111        }
112        this.inf = inflater;
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 byteCount} bytes of decompressed data and stores it in
140     * {@code buffer} starting at {@code offset}.
141     *
142     * @return Number of uncompressed bytes read
143     */
144    @Override
145    public int read(byte[] buffer, int offset, int byteCount) throws IOException {
146        checkClosed();
147        Arrays.checkOffsetAndCount(buffer.length, offset, byteCount);
148
149        if (byteCount == 0) {
150            return 0;
151        }
152
153        if (eof) {
154            return -1;
155        }
156
157        do {
158            if (inf.needsInput()) {
159                fill();
160            }
161            // Invariant: if reading returns -1 or throws, eof must be true.
162            // It may also be true if the next read() should return -1.
163            try {
164                int result = inf.inflate(buffer, offset, byteCount);
165                eof = inf.finished();
166                if (result > 0) {
167                    return result;
168                } else if (eof) {
169                    return -1;
170                } else if (inf.needsDictionary()) {
171                    eof = true;
172                    return -1;
173                } else if (len == -1) {
174                    eof = true;
175                    throw new EOFException();
176                    // If result == 0, fill() and try again
177                }
178            } catch (DataFormatException e) {
179                eof = true;
180                if (len == -1) {
181                    throw new EOFException();
182                }
183                throw (IOException) (new IOException().initCause(e));
184            }
185        } while (true);
186    }
187
188    /**
189     * Fills the input buffer with data to be decompressed.
190     *
191     * @throws IOException
192     *             if an {@code IOException} occurs.
193     */
194    protected void fill() throws IOException {
195        checkClosed();
196        // BEGIN android-only
197        if (nativeEndBufSize > 0) {
198            ZipFile.RAFStream is = (ZipFile.RAFStream)in;
199            synchronized (is.mSharedRaf) {
200                long len = is.mLength - is.mOffset;
201                if (len > nativeEndBufSize) len = nativeEndBufSize;
202                int cnt = inf.setFileInput(is.mSharedRaf.getFD(), is.mOffset, (int)nativeEndBufSize);
203                is.skip(cnt);
204            }
205        } else {
206            if ((len = in.read(buf)) > 0) {
207                inf.setInput(buf, 0, len);
208            }
209        }
210        // END android-only
211    }
212
213    /**
214     * Skips up to {@code byteCount} bytes of uncompressed data.
215     *
216     * @param byteCount the number of bytes to skip.
217     * @return the number of uncompressed bytes skipped.
218     * @throws IllegalArgumentException if {@code byteCount < 0}.
219     * @throws IOException if an error occurs skipping.
220     */
221    @Override
222    public long skip(long byteCount) throws IOException {
223        if (byteCount < 0) {
224            throw new IllegalArgumentException("byteCount < 0");
225        }
226        return Streams.skipByReading(this, byteCount);
227    }
228
229    /**
230     * Returns 0 when when this stream has exhausted its input; and 1 otherwise.
231     * A result of 1 does not guarantee that further bytes can be returned,
232     * with or without blocking.
233     *
234     * <p>Although consistent with the RI, this behavior is inconsistent with
235     * {@link InputStream#available()}, and violates the <a
236     * href="http://en.wikipedia.org/wiki/Liskov_substitution_principle">Liskov
237     * Substitution Principle</a>. This method should not be used.
238     *
239     * @return 0 if no further bytes are available. Otherwise returns 1,
240     *         which suggests (but does not guarantee) that additional bytes are
241     *         available.
242     * @throws IOException if this stream is closed or an error occurs
243     */
244    @Override
245    public int available() throws IOException {
246        checkClosed();
247        if (eof) {
248            return 0;
249        }
250        return 1;
251    }
252
253    /**
254     * Closes the input stream.
255     *
256     * @throws IOException
257     *             If an error occurs closing the input stream.
258     */
259    @Override
260    public void close() throws IOException {
261        if (!closed) {
262            inf.end();
263            closed = true;
264            eof = true;
265            super.close();
266        }
267    }
268
269    /**
270     * Marks the current position in the stream. This implementation overrides
271     * the super type implementation to do nothing at all.
272     *
273     * @param readlimit
274     *            of no use.
275     */
276    @Override
277    public void mark(int readlimit) {
278        // do nothing
279    }
280
281    /**
282     * Reset the position of the stream to the last marked position. This
283     * implementation overrides the supertype implementation and always throws
284     * an {@link IOException IOException} when called.
285     *
286     * @throws IOException
287     *             if the method is called
288     */
289    @Override
290    public void reset() throws IOException {
291        throw new IOException();
292    }
293
294    /**
295     * Returns whether the receiver implements {@code mark} semantics. This type
296     * does not support {@code mark()}, so always responds {@code false}.
297     *
298     * @return false, always
299     */
300    @Override
301    public boolean markSupported() {
302        return false;
303    }
304
305    private void checkClosed() throws IOException {
306        if (closed) {
307            throw new IOException("Stream is closed");
308        }
309    }
310}
311