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 
27 package java.util.zip;
28 
29 import java.io.FilterInputStream;
30 import java.io.InputStream;
31 import 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 
45 public 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      */
ensureOpen()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      */
DeflaterInputStream(InputStream in)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      */
DeflaterInputStream(InputStream in, Deflater defl)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      */
DeflaterInputStream(InputStream in, Deflater defl, int bufLen)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      */
close()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      */
read()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      */
read(byte[] b, int off, int len)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      */
skip(long n)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      */
available()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      */
markSupported()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      */
mark(int limit)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      */
reset()295     public void reset() throws IOException {
296         throw new IOException("mark/reset not supported");
297     }
298 }
299