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.FilterInputStream;
21import java.io.IOException;
22import java.io.InputStream;
23import java.util.Arrays;
24import libcore.io.Streams;
25
26/**
27 * An {@code InputStream} filter to compress data. Callers read
28 * compressed data in the "deflate" format from the uncompressed
29 * underlying stream.
30 * @since 1.6
31 */
32public class DeflaterInputStream extends FilterInputStream {
33    private static final int DEFAULT_BUFFER_SIZE = 1024;
34
35    protected final Deflater def;
36    protected final byte[] buf;
37
38    private boolean closed = false;
39    private boolean available = true;
40
41    /**
42     * Constructs a {@code DeflaterInputStream} with a new {@code Deflater} and an
43     * implementation-defined default internal buffer size. {@code in} is a source of
44     * uncompressed data, and this stream will be a source of compressed data.
45     *
46     * @param in the source {@code InputStream}
47     */
48    public DeflaterInputStream(InputStream in) {
49        this(in, new Deflater(), DEFAULT_BUFFER_SIZE);
50    }
51
52    /**
53     * Constructs a {@code DeflaterInputStream} with the given {@code Deflater} and an
54     * implementation-defined default internal buffer size. {@code in} is a source of
55     * uncompressed data, and this stream will be a source of compressed data.
56     *
57     * @param in the source {@code InputStream}
58     * @param deflater the {@code Deflater} to be used for compression
59     */
60    public DeflaterInputStream(InputStream in, Deflater deflater) {
61        this(in, deflater, DEFAULT_BUFFER_SIZE);
62    }
63
64    /**
65     * Constructs a {@code DeflaterInputStream} with the given {@code Deflater} and
66     * given internal buffer size. {@code in} is a source of
67     * uncompressed data, and this stream will be a source of compressed data.
68     *
69     * @param in the source {@code InputStream}
70     * @param deflater the {@code Deflater} to be used for compression
71     * @param bufferSize the length in bytes of the internal buffer
72     */
73    public DeflaterInputStream(InputStream in, Deflater deflater, int bufferSize) {
74        super(in);
75        if (in == null) {
76            throw new NullPointerException("in == null");
77        } else if (deflater == null) {
78            throw new NullPointerException("deflater == null");
79        }
80        if (bufferSize <= 0) {
81            throw new IllegalArgumentException();
82        }
83        this.def = deflater;
84        this.buf = new byte[bufferSize];
85    }
86
87    /**
88     * Closes the underlying input stream and discards any remaining uncompressed
89     * data.
90     */
91    @Override
92    public void close() throws IOException {
93        closed = true;
94        def.end();
95        in.close();
96    }
97
98    /**
99     * Reads a byte from the compressed input stream. The result will be a byte of compressed
100     * data corresponding to an uncompressed byte or bytes read from the underlying stream.
101     *
102     * @return the byte or -1 if the end of the stream has been reached.
103     */
104    @Override public int read() throws IOException {
105        return Streams.readSingleByte(this);
106    }
107
108    /**
109     * Reads compressed data into a byte buffer. The result will be bytes of compressed
110     * data corresponding to an uncompressed byte or bytes read from the underlying stream.
111     * @return the number of bytes read or -1 if the end of the compressed input
112     *         stream has been reached.
113     */
114    @Override public int read(byte[] buffer, int offset, int byteCount) throws IOException {
115        checkClosed();
116        Arrays.checkOffsetAndCount(buffer.length, offset, byteCount);
117        if (byteCount == 0) {
118            return 0;
119        }
120
121        if (!available) {
122            return -1;
123        }
124
125        int count = 0;
126        while (count < byteCount && !def.finished()) {
127            if (def.needsInput()) {
128                // read data from input stream
129                int bytesRead = in.read(buf);
130                if (bytesRead == -1) {
131                    def.finish();
132                } else {
133                    def.setInput(buf, 0, bytesRead);
134                }
135            }
136            int bytesDeflated = def.deflate(buf, 0, Math.min(buf.length, byteCount - count));
137            if (bytesDeflated == -1) {
138                break;
139            }
140            System.arraycopy(buf, 0, buffer, offset + count, bytesDeflated);
141            count += bytesDeflated;
142        }
143        if (count == 0) {
144            count = -1;
145            available = false;
146        }
147        return count;
148    }
149
150    /**
151     * {@inheritDoc}
152     * <p>Note: if {@code n > Integer.MAX_VALUE}, this stream will only attempt to
153     * skip {@code Integer.MAX_VALUE} bytes.
154     */
155    @Override
156    public long skip(long byteCount) throws IOException {
157        byteCount = Math.min(Integer.MAX_VALUE, byteCount);
158        return Streams.skipByReading(this, byteCount);
159    }
160
161    /**
162     * Returns 0 when when this stream has exhausted its input; and 1 otherwise.
163     * A result of 1 does not guarantee that further bytes can be returned,
164     * with or without blocking.
165     *
166     * <p>Although consistent with the RI, this behavior is inconsistent with
167     * {@link InputStream#available()}, and violates the <a
168     * href="http://en.wikipedia.org/wiki/Liskov_substitution_principle">Liskov
169     * Substitution Principle</a>. This method should not be used.
170     *
171     * @return 0 if no further bytes are available. Otherwise returns 1,
172     *         which suggests (but does not guarantee) that additional bytes are
173     *         available.
174     * @throws IOException if this stream is closed or an error occurs
175     */
176    @Override
177    public int available() throws IOException {
178        checkClosed();
179        return available ? 1 : 0;
180    }
181
182    /**
183     * Returns false because {@code DeflaterInputStream} does not support
184     * {@code mark}/{@code reset}.
185     */
186    @Override
187    public boolean markSupported() {
188        return false;
189    }
190
191    /**
192     * This operation is not supported and does nothing.
193     */
194    @Override
195    public void mark(int limit) {
196    }
197
198    /**
199     * This operation is not supported and throws {@code IOException}.
200     */
201    @Override
202    public void reset() throws IOException {
203        throw new IOException();
204    }
205
206    private void checkClosed() throws IOException {
207        if (closed) {
208            throw new IOException("Stream is closed");
209        }
210    }
211}
212