001 /*
002 * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package java.util.zip;
027
028 import java.io.FilterInputStream;
029 import java.io.InputStream;
030 import java.io.IOException;
031
032 /**
033 * Implements an input stream filter for compressing data in the "deflate"
034 * compression format.
035 *
036 * @version 1.7
037 * @since 1.6
038 * @author David R Tribble (david@tribble.com)
039 *
040 * @see DeflaterOutputStream
041 * @see InflaterOutputStream
042 * @see InflaterInputStream
043 */
044
045 public class DeflaterInputStream extends FilterInputStream {
046 /** Compressor for this stream. */
047 protected final Deflater def;
048
049 /** Input buffer for reading compressed data. */
050 protected final byte[] buf;
051
052 /** Temporary read buffer. */
053 private byte[] rbuf = new byte[1];
054
055 /** Default compressor is used. */
056 private boolean usesDefaultDeflater = false;
057
058 /** End of the underlying input stream has been reached. */
059 private boolean reachEOF = false;
060
061 /**
062 * Check to make sure that this stream has not been closed.
063 */
064 private void ensureOpen() throws IOException {
065 if (in == null) {
066 throw new IOException("Stream closed");
067 }
068 }
069
070 /**
071 * Creates a new input stream with a default compressor and buffer
072 * size.
073 *
074 * @param in input stream to read the uncompressed data to
075 * @throws NullPointerException if {@code in} is null
076 */
077 public DeflaterInputStream(InputStream in) {
078 this (in, new Deflater());
079 usesDefaultDeflater = true;
080 }
081
082 /**
083 * Creates a new input stream with the specified compressor and a
084 * default buffer size.
085 *
086 * @param in input stream to read the uncompressed data to
087 * @param defl compressor ("deflater") for this stream
088 * @throws NullPointerException if {@code in} or {@code defl} is null
089 */
090 public DeflaterInputStream(InputStream in, Deflater defl) {
091 this (in, defl, 512);
092 }
093
094 /**
095 * Creates a new input stream with the specified compressor and buffer
096 * size.
097 *
098 * @param in input stream to read the uncompressed data to
099 * @param defl compressor ("deflater") for this stream
100 * @param bufLen compression buffer size
101 * @throws IllegalArgumentException if {@code bufLen} is <= 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} > {@code b.length -
168 * off}
169 * @throws IOException if an I/O error occurs or if this input stream is
170 * already closed
171 */
172 public int read(byte[] b, int off, int len) throws IOException {
173 // Sanity checks
174 ensureOpen();
175 if (b == null) {
176 throw new NullPointerException("Null buffer for read");
177 } else if (off < 0 || len < 0 || len > b.length - off) {
178 throw new IndexOutOfBoundsException();
179 } else if (len == 0) {
180 return 0;
181 }
182
183 // Read and compress (deflate) input data bytes
184 int cnt = 0;
185 while (len > 0 && !def.finished()) {
186 int n;
187
188 // Read data from the input stream
189 if (def.needsInput()) {
190 n = in.read(buf, 0, buf.length);
191 if (n < 0) {
192 // End of the input stream reached
193 def.finish();
194 } else if (n > 0) {
195 def.setInput(buf, 0, n);
196 }
197 }
198
199 // Compress the input data, filling the read buffer
200 n = def.deflate(b, off, len);
201 cnt += n;
202 off += n;
203 len -= n;
204 }
205 if (cnt == 0 && def.finished()) {
206 reachEOF = true;
207 cnt = -1;
208 }
209
210 return cnt;
211 }
212
213 /**
214 * Skips over and discards data from the input stream.
215 * This method may block until the specified number of bytes are read and
216 * skipped. <em>Note:</em> While {@code n} is given as a {@code long},
217 * the maximum number of bytes which can be skipped is
218 * {@code Integer.MAX_VALUE}.
219 *
220 * @param n number of bytes to be skipped
221 * @return the actual number of bytes skipped
222 * @throws IOException if an I/O error occurs or if this stream is
223 * already closed
224 */
225 public long skip(long n) throws IOException {
226 if (n < 0) {
227 throw new IllegalArgumentException("negative skip length");
228 }
229 ensureOpen();
230
231 // Skip bytes by repeatedly decompressing small blocks
232 if (rbuf.length < 512)
233 rbuf = new byte[512];
234
235 int total = (int) Math.min(n, Integer.MAX_VALUE);
236 long cnt = 0;
237 while (total > 0) {
238 // Read a small block of uncompressed bytes
239 int len = read(rbuf, 0, (total <= rbuf.length ? total
240 : rbuf.length));
241
242 if (len < 0) {
243 break;
244 }
245 cnt += len;
246 total -= len;
247 }
248 return cnt;
249 }
250
251 /**
252 * Returns 0 after EOF has been reached, otherwise always return 1.
253 * <p>
254 * Programs should not count on this method to return the actual number
255 * of bytes that could be read without blocking
256 * @return zero after the end of the underlying input stream has been
257 * reached, otherwise always returns 1
258 * @throws IOException if an I/O error occurs or if this stream is
259 * already closed
260 */
261 public int available() throws IOException {
262 ensureOpen();
263 if (reachEOF) {
264 return 0;
265 }
266 return 1;
267 }
268
269 /**
270 * Always returns {@code false} because this input stream does not support
271 * the {@link #mark mark()} and {@link #reset reset()} methods.
272 *
273 * @return false, always
274 */
275 public boolean markSupported() {
276 return false;
277 }
278
279 /**
280 * <i>This operation is not supported</i>.
281 *
282 * @param limit maximum bytes that can be read before invalidating the position marker
283 */
284 public void mark(int limit) {
285 // Operation not supported
286 }
287
288 /**
289 * <i>This operation is not supported</i>.
290 *
291 * @throws IOException always thrown
292 */
293 public void reset() throws IOException {
294 throw new IOException("mark/reset not supported");
295 }
296 }
|