/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package java.util.zip; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import libcore.io.Streams; /** * An {@code InputStream} filter to compress data. Callers read * compressed data in the "deflate" format from the uncompressed * underlying stream. * @since 1.6 */ public class DeflaterInputStream extends FilterInputStream { private static final int DEFAULT_BUFFER_SIZE = 1024; protected final Deflater def; protected final byte[] buf; private boolean closed = false; private boolean available = true; /** * Constructs a {@code DeflaterInputStream} with a new {@code Deflater} and an * implementation-defined default internal buffer size. {@code in} is a source of * uncompressed data, and this stream will be a source of compressed data. * * @param in the source {@code InputStream} */ public DeflaterInputStream(InputStream in) { this(in, new Deflater(), DEFAULT_BUFFER_SIZE); } /** * Constructs a {@code DeflaterInputStream} with the given {@code Deflater} and an * implementation-defined default internal buffer size. {@code in} is a source of * uncompressed data, and this stream will be a source of compressed data. * * @param in the source {@code InputStream} * @param deflater the {@code Deflater} to be used for compression */ public DeflaterInputStream(InputStream in, Deflater deflater) { this(in, deflater, DEFAULT_BUFFER_SIZE); } /** * Constructs a {@code DeflaterInputStream} with the given {@code Deflater} and * given internal buffer size. {@code in} is a source of * uncompressed data, and this stream will be a source of compressed data. * * @param in the source {@code InputStream} * @param deflater the {@code Deflater} to be used for compression * @param bufferSize the length in bytes of the internal buffer */ public DeflaterInputStream(InputStream in, Deflater deflater, int bufferSize) { super(in); if (in == null) { throw new NullPointerException("in == null"); } else if (deflater == null) { throw new NullPointerException("deflater == null"); } if (bufferSize <= 0) { throw new IllegalArgumentException(); } this.def = deflater; this.buf = new byte[bufferSize]; } /** * Closes the underlying input stream and discards any remaining uncompressed * data. */ @Override public void close() throws IOException { closed = true; def.end(); in.close(); } /** * Reads a byte from the compressed input stream. The result will be a byte of compressed * data corresponding to an uncompressed byte or bytes read from the underlying stream. * * @return the byte or -1 if the end of the stream has been reached. */ @Override public int read() throws IOException { return Streams.readSingleByte(this); } /** * Reads compressed data into a byte buffer. The result will be bytes of compressed * data corresponding to an uncompressed byte or bytes read from the underlying stream. * @return the number of bytes read or -1 if the end of the compressed input * stream has been reached. */ @Override public int read(byte[] buffer, int offset, int byteCount) throws IOException { checkClosed(); Arrays.checkOffsetAndCount(buffer.length, offset, byteCount); if (byteCount == 0) { return 0; } if (!available) { return -1; } int count = 0; while (count < byteCount && !def.finished()) { if (def.needsInput()) { // read data from input stream int bytesRead = in.read(buf); if (bytesRead == -1) { def.finish(); } else { def.setInput(buf, 0, bytesRead); } } int bytesDeflated = def.deflate(buf, 0, Math.min(buf.length, byteCount - count)); if (bytesDeflated == -1) { break; } System.arraycopy(buf, 0, buffer, offset + count, bytesDeflated); count += bytesDeflated; } if (count == 0) { count = -1; available = false; } return count; } /** * {@inheritDoc} * <p>Note: if {@code n > Integer.MAX_VALUE}, this stream will only attempt to * skip {@code Integer.MAX_VALUE} bytes. */ @Override public long skip(long byteCount) throws IOException { byteCount = Math.min(Integer.MAX_VALUE, byteCount); return Streams.skipByReading(this, byteCount); } /** * Returns 0 when when this stream has exhausted its input; and 1 otherwise. * A result of 1 does not guarantee that further bytes can be returned, * with or without blocking. * * <p>Although consistent with the RI, this behavior is inconsistent with * {@link InputStream#available()}, and violates the <a * href="http://en.wikipedia.org/wiki/Liskov_substitution_principle">Liskov * Substitution Principle</a>. This method should not be used. * * @return 0 if no further bytes are available. Otherwise returns 1, * which suggests (but does not guarantee) that additional bytes are * available. * @throws IOException if this stream is closed or an error occurs */ @Override public int available() throws IOException { checkClosed(); return available ? 1 : 0; } /** * Returns false because {@code DeflaterInputStream} does not support * {@code mark}/{@code reset}. */ @Override public boolean markSupported() { return false; } /** * This operation is not supported and does nothing. */ @Override public void mark(int limit) { } /** * This operation is not supported and throws {@code IOException}. */ @Override public void reset() throws IOException { throw new IOException(); } private void checkClosed() throws IOException { if (closed) { throw new IOException("Stream is closed"); } } }