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