DeflaterInputStream.java revision 6975f84c2ed72e1e26d20190b6f318718c849008
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 only, as
8 * published by the Free Software Foundation.  Oracle designates this
9 * particular file as subject to the "Classpath" exception as provided
10 * by Oracle in the LICENSE file that accompanied this code.
11 *
12 * This code is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 * version 2 for more details (a copy is included in the LICENSE file that
16 * accompanied this code).
17 *
18 * You should have received a copy of the GNU General Public License version
19 * 2 along with this work; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23 * or visit www.oracle.com if you need additional information or have any
24 * questions.
25 */
26
27package java.util.zip;
28
29import java.io.FilterInputStream;
30import java.io.InputStream;
31import java.io.IOException;
32
33/**
34 * Implements an input stream filter for compressing data in the "deflate"
35 * compression format.
36 *
37 * @since       1.6
38 * @author      David R Tribble (david@tribble.com)
39 *
40 * @see DeflaterOutputStream
41 * @see InflaterOutputStream
42 * @see InflaterInputStream
43 */
44
45public class DeflaterInputStream extends FilterInputStream {
46    /** Compressor for this stream. */
47    protected final Deflater def;
48
49    /** Input buffer for reading compressed data. */
50    protected final byte[] buf;
51
52    /** Temporary read buffer. */
53    private byte[] rbuf = new byte[1];
54
55    /** Default compressor is used. */
56    private boolean usesDefaultDeflater = false;
57
58    /** End of the underlying input stream has been reached. */
59    private boolean reachEOF = false;
60
61    /**
62     * Check to make sure that this stream has not been closed.
63     */
64    private void ensureOpen() throws IOException {
65        if (in == null) {
66            throw new IOException("Stream closed");
67        }
68    }
69
70    /**
71     * Creates a new input stream with a default compressor and buffer
72     * size.
73     *
74     * @param in input stream to read the uncompressed data to
75     * @throws NullPointerException if {@code in} is null
76     */
77    public DeflaterInputStream(InputStream in) {
78        this(in, new Deflater());
79        usesDefaultDeflater = true;
80    }
81
82    /**
83     * Creates a new input stream with the specified compressor and a
84     * default buffer size.
85     *
86     * @param in input stream to read the uncompressed data to
87     * @param defl compressor ("deflater") for this stream
88     * @throws NullPointerException if {@code in} or {@code defl} is null
89     */
90    public DeflaterInputStream(InputStream in, Deflater defl) {
91        this(in, defl, 512);
92    }
93
94    /**
95     * Creates a new input stream with the specified compressor and buffer
96     * size.
97     *
98     * @param in input stream to read the uncompressed data to
99     * @param defl compressor ("deflater") for this stream
100     * @param bufLen compression buffer size
101     * @throws IllegalArgumentException if {@code bufLen <= 0}
102     * @throws NullPointerException if {@code in} or {@code defl} is null
103     */
104    public DeflaterInputStream(InputStream in, Deflater defl, int bufLen) {
105        super(in);
106
107        // Sanity checks
108        if (in == null)
109            throw new NullPointerException("Null input");
110        if (defl == null)
111            throw new NullPointerException("Null deflater");
112        if (bufLen < 1)
113            throw new IllegalArgumentException("Buffer size < 1");
114
115        // Initialize
116        def = defl;
117        buf = new byte[bufLen];
118    }
119
120    /**
121     * Closes this input stream and its underlying input stream, discarding
122     * any pending uncompressed data.
123     *
124     * @throws IOException if an I/O error occurs
125     */
126    public void close() throws IOException {
127        if (in != null) {
128            try {
129                // Clean up
130                if (usesDefaultDeflater) {
131                    def.end();
132                }
133
134                in.close();
135            } finally {
136                in = null;
137            }
138        }
139    }
140
141    /**
142     * Reads a single byte of compressed data from the input stream.
143     * This method will block until some input can be read and compressed.
144     *
145     * @return a single byte of compressed data, or -1 if the end of the
146     * uncompressed input stream is reached
147     * @throws IOException if an I/O error occurs or if this stream is
148     * already closed
149     */
150    public int read() throws IOException {
151        // Read a single byte of compressed data
152        int len = read(rbuf, 0, 1);
153        if (len <= 0)
154            return -1;
155        return (rbuf[0] & 0xFF);
156    }
157
158    /**
159     * Reads compressed data into a byte array.
160     * This method will block until some input can be read and compressed.
161     *
162     * @param b buffer into which the data is read
163     * @param off starting offset of the data within {@code b}
164     * @param len maximum number of compressed bytes to read into {@code b}
165     * @return the actual number of bytes read, or -1 if the end of the
166     * uncompressed input stream is reached
167     * @throws IndexOutOfBoundsException  if {@code len > b.length - off}
168     * @throws IOException if an I/O error occurs or if this input stream is
169     * already closed
170     */
171    public int read(byte[] b, int off, int len) throws IOException {
172        // Sanity checks
173        ensureOpen();
174        if (b == null) {
175            throw new NullPointerException("Null buffer for read");
176        } else if (off < 0 || len < 0 || len > b.length - off) {
177            throw new IndexOutOfBoundsException();
178        } else if (len == 0) {
179            return 0;
180        }
181
182        // Read and compress (deflate) input data bytes
183        int cnt = 0;
184        while (len > 0 && !def.finished()) {
185            int n;
186
187            // Read data from the input stream
188            if (def.needsInput()) {
189                n = in.read(buf, 0, buf.length);
190                if (n < 0) {
191                    // End of the input stream reached
192                    def.finish();
193                } else if (n > 0) {
194                    def.setInput(buf, 0, n);
195                }
196            }
197
198            // Compress the input data, filling the read buffer
199            n = def.deflate(b, off, len);
200            cnt += n;
201            off += n;
202            len -= n;
203        }
204        // Android-changed: set reachEOF eagerly (not just when the number of bytes is zero).
205        // so that available is more accurate.
206        if (def.finished()) {
207            reachEOF =true;
208            if (cnt == 0) {
209                cnt = -1;
210            }
211        }
212
213        return cnt;
214    }
215
216    /**
217     * Skips over and discards data from the input stream.
218     * This method may block until the specified number of bytes are read and
219     * skipped. <em>Note:</em> While {@code n} is given as a {@code long},
220     * the maximum number of bytes which can be skipped is
221     * {@code Integer.MAX_VALUE}.
222     *
223     * @param n number of bytes to be skipped
224     * @return the actual number of bytes skipped
225     * @throws IOException if an I/O error occurs or if this stream is
226     * already closed
227     */
228    public long skip(long n) throws IOException {
229        if (n < 0) {
230            throw new IllegalArgumentException("negative skip length");
231        }
232        ensureOpen();
233
234        // Skip bytes by repeatedly decompressing small blocks
235        if (rbuf.length < 512)
236            rbuf = new byte[512];
237
238        int total = (int)Math.min(n, Integer.MAX_VALUE);
239        long cnt = 0;
240        while (total > 0) {
241            // Read a small block of uncompressed bytes
242            int len = read(rbuf, 0, (total <= rbuf.length ? total : rbuf.length));
243
244            if (len < 0) {
245                break;
246            }
247            cnt += len;
248            total -= len;
249        }
250        return cnt;
251    }
252
253    /**
254     * Returns 0 after EOF has been reached, otherwise always return 1.
255     * <p>
256     * Programs should not count on this method to return the actual number
257     * of bytes that could be read without blocking
258     * @return zero after the end of the underlying input stream has been
259     * reached, otherwise always returns 1
260     * @throws IOException if an I/O error occurs or if this stream is
261     * already closed
262     */
263    public int available() throws IOException {
264        ensureOpen();
265        if (reachEOF) {
266            return 0;
267        }
268        return 1;
269    }
270
271    /**
272     * Always returns {@code false} because this input stream does not support
273     * the {@link #mark mark()} and {@link #reset reset()} methods.
274     *
275     * @return false, always
276     */
277    public boolean markSupported() {
278        return false;
279    }
280
281    /**
282     * <i>This operation is not supported</i>.
283     *
284     * @param limit maximum bytes that can be read before invalidating the position marker
285     */
286    public void mark(int limit) {
287        // Operation not supported
288    }
289
290    /**
291     * <i>This operation is not supported</i>.
292     *
293     * @throws IOException always thrown
294     */
295    public void reset() throws IOException {
296        throw new IOException("mark/reset not supported");
297    }
298}
299