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 dalvik.system.CloseGuard;
21import java.io.FileDescriptor;
22import java.util.Arrays;
23
24/**
25 * This class decompresses data that was compressed using the <i>DEFLATE</i>
26 * algorithm (see <a href="http://www.gzip.org/algorithm.txt">specification</a>).
27 *
28 * <p>It is usually more convenient to use {@link InflaterInputStream}.
29 *
30 * <p>To decompress an in-memory {@code byte[]} to another in-memory {@code byte[]} manually:
31 * <pre>
32 *     byte[] compressedBytes = ...
33 *     int decompressedByteCount = ... // From your format's metadata.
34 *     Inflater inflater = new Inflater();
35 *     inflater.setInput(compressedBytes, 0, compressedBytes.length);
36 *     byte[] decompressedBytes = new byte[decompressedByteCount];
37 *     if (inflater.inflate(decompressedBytes) != decompressedByteCount) {
38 *         throw new AssertionError();
39 *     }
40 *     inflater.end();
41 * </pre>
42 * <p>In situations where you don't have all the input in one array (or have so much
43 * input that you want to feed it to the inflater in chunks), it's possible to call
44 * {@link #setInput} repeatedly, but you're much better off using {@link InflaterInputStream}
45 * to handle all this for you.
46 *
47 * <p>If you don't know how big the decompressed data will be, you can call {@link #inflate}
48 * repeatedly on a temporary buffer, copying the bytes to a {@link java.io.ByteArrayOutputStream},
49 * but this is probably another sign you'd be better off using {@link InflaterInputStream}.
50 */
51public class Inflater {
52
53    private int inLength;
54
55    private int inRead; // Set by inflateImpl.
56    private boolean finished; // Set by inflateImpl.
57    private boolean needsDictionary; // Set by inflateImpl.
58
59    private long streamHandle = -1;
60
61    private final CloseGuard guard = CloseGuard.get();
62
63    /**
64     * This constructor creates an inflater that expects a header from the input
65     * stream. Use {@link #Inflater(boolean)} if the input comes without a ZLIB
66     * header.
67     */
68    public Inflater() {
69        this(false);
70    }
71
72    /**
73     * This constructor allows to create an inflater that expects no header from
74     * the input stream.
75     *
76     * @param noHeader
77     *            {@code true} indicates that no ZLIB header comes with the
78     *            input.
79     */
80    public Inflater(boolean noHeader) {
81        streamHandle = createStream(noHeader);
82        guard.open("end");
83    }
84
85    private native long createStream(boolean noHeader1);
86
87    /**
88     * Releases resources associated with this {@code Inflater}. Any unused
89     * input or output is discarded. This method should be called explicitly in
90     * order to free native resources as soon as possible. After {@code end()} is
91     * called, other methods will typically throw {@code IllegalStateException}.
92     */
93    public synchronized void end() {
94        guard.close();
95        if (streamHandle != -1) {
96            endImpl(streamHandle);
97            inRead = 0;
98            inLength = 0;
99            streamHandle = -1;
100        }
101    }
102
103    private native void endImpl(long handle);
104
105    @Override protected void finalize() {
106        try {
107            if (guard != null) {
108                guard.warnIfOpen();
109            }
110            end();
111        } finally {
112            try {
113                super.finalize();
114            } catch (Throwable t) {
115                throw new AssertionError(t);
116            }
117        }
118    }
119
120    /**
121     * Indicates if the {@code Inflater} has inflated the entire deflated
122     * stream. If deflated bytes remain and {@link #needsInput} returns {@code
123     * true} this method will return {@code false}. This method should be
124     * called after all deflated input is supplied to the {@code Inflater}.
125     *
126     * @return {@code true} if all input has been inflated, {@code false}
127     *         otherwise.
128     */
129    public synchronized boolean finished() {
130        return finished;
131    }
132
133    /**
134     * Returns the {@link Adler32} checksum of the bytes inflated so far, or the
135     * checksum of the preset dictionary if {@link #needsDictionary} returns true.
136     */
137    public synchronized int getAdler() {
138        checkOpen();
139        return getAdlerImpl(streamHandle);
140    }
141
142    private native int getAdlerImpl(long handle);
143
144    /**
145     * Returns the total number of bytes read by the {@code Inflater}. This
146     * method is the same as {@link #getTotalIn} except that it returns a
147     * {@code long} value instead of an integer.
148     */
149    public synchronized long getBytesRead() {
150        checkOpen();
151        return getTotalInImpl(streamHandle);
152    }
153
154    /**
155     * Returns a the total number of bytes written by this {@code Inflater}. This
156     * method is the same as {@code getTotalOut} except it returns a
157     * {@code long} value instead of an integer.
158     */
159    public synchronized long getBytesWritten() {
160        checkOpen();
161        return getTotalOutImpl(streamHandle);
162    }
163
164    /**
165     * Returns the number of bytes of current input remaining to be read by this
166     * inflater.
167     */
168    public synchronized int getRemaining() {
169        return inLength - inRead;
170    }
171
172    /**
173     * Returns the offset of the next byte to read in the underlying buffer.
174     *
175     * For internal use only.
176     */
177    synchronized int getCurrentOffset() {
178        return inRead;
179    }
180
181    /**
182     * Returns the total number of bytes of input read by this {@code Inflater}. This
183     * method is limited to 32 bits; use {@link #getBytesRead} instead.
184     */
185    public synchronized int getTotalIn() {
186        checkOpen();
187        return (int) Math.min(getTotalInImpl(streamHandle), (long) Integer.MAX_VALUE);
188    }
189
190    private native long getTotalInImpl(long handle);
191
192    /**
193     * Returns the total number of bytes written to the output buffer by this {@code
194     * Inflater}. The method is limited to 32 bits; use {@link #getBytesWritten} instead.
195     */
196    public synchronized int getTotalOut() {
197        checkOpen();
198        return (int) Math.min(getTotalOutImpl(streamHandle), (long) Integer.MAX_VALUE);
199    }
200
201    private native long getTotalOutImpl(long handle);
202
203    /**
204     * Inflates bytes from the current input and stores them in {@code buf}.
205     *
206     * @param buf
207     *            the buffer where decompressed data bytes are written.
208     * @return the number of bytes inflated.
209     * @throws DataFormatException
210     *             if the underlying stream is corrupted or was not compressed
211     *             using a {@code Deflater}.
212     */
213    public int inflate(byte[] buf) throws DataFormatException {
214        return inflate(buf, 0, buf.length);
215    }
216
217    /**
218     * Inflates up to {@code byteCount} bytes from the current input and stores them in
219     * {@code buf} starting at {@code offset}.
220     *
221     * @throws DataFormatException
222     *             if the underlying stream is corrupted or was not compressed
223     *             using a {@code Deflater}.
224     * @return the number of bytes inflated.
225     */
226    public synchronized int inflate(byte[] buf, int offset, int byteCount) throws DataFormatException {
227        Arrays.checkOffsetAndCount(buf.length, offset, byteCount);
228
229        checkOpen();
230
231        if (needsInput()) {
232            return 0;
233        }
234
235        boolean neededDict = needsDictionary;
236        needsDictionary = false;
237        int result = inflateImpl(buf, offset, byteCount, streamHandle);
238        if (needsDictionary && neededDict) {
239            throw new DataFormatException("Needs dictionary");
240        }
241        return result;
242    }
243
244    private native int inflateImpl(byte[] buf, int offset, int byteCount, long handle);
245
246    /**
247     * Returns true if the input bytes were compressed with a preset
248     * dictionary. This method should be called if the first call to {@link #inflate} returns 0,
249     * to determine whether a dictionary is required. If so, {@link #setDictionary}
250     * should be called with the appropriate dictionary before calling {@code
251     * inflate} again. Use {@link #getAdler} to determine which dictionary is required.
252     */
253    public synchronized boolean needsDictionary() {
254        return needsDictionary;
255    }
256
257    /**
258     * Returns true if {@link #setInput} must be called before inflation can continue.
259     */
260    public synchronized boolean needsInput() {
261        return inRead == inLength;
262    }
263
264    /**
265     * Resets this {@code Inflater}. Should be called prior to inflating a new
266     * set of data.
267     */
268    public synchronized void reset() {
269        checkOpen();
270        finished = false;
271        needsDictionary = false;
272        inLength = inRead = 0;
273        resetImpl(streamHandle);
274    }
275
276    private native void resetImpl(long handle);
277
278    /**
279     * Sets the preset dictionary to be used for inflation to {@code dictionary}.
280     * See {@link #needsDictionary} for details.
281     */
282    public synchronized void setDictionary(byte[] dictionary) {
283        setDictionary(dictionary, 0, dictionary.length);
284    }
285
286    /**
287     * Sets the preset dictionary to be used for inflation to a subsequence of {@code dictionary}
288     * starting at {@code offset} and continuing for {@code byteCount} bytes. See {@link
289     * #needsDictionary} for details.
290     */
291    public synchronized void setDictionary(byte[] dictionary, int offset, int byteCount) {
292        checkOpen();
293        Arrays.checkOffsetAndCount(dictionary.length, offset, byteCount);
294        setDictionaryImpl(dictionary, offset, byteCount, streamHandle);
295    }
296
297    private native void setDictionaryImpl(byte[] dictionary, int offset, int byteCount, long handle);
298
299    /**
300     * Sets the current input to to be decompressed. This method should only be
301     * called if {@link #needsInput} returns {@code true}.
302     */
303    public synchronized void setInput(byte[] buf) {
304        setInput(buf, 0, buf.length);
305    }
306
307    /**
308     * Sets the current input to to be decompressed. This method should only be
309     * called if {@link #needsInput} returns {@code true}.
310     */
311    public synchronized void setInput(byte[] buf, int offset, int byteCount) {
312        checkOpen();
313        Arrays.checkOffsetAndCount(buf.length, offset, byteCount);
314        inRead = 0;
315        inLength = byteCount;
316        setInputImpl(buf, offset, byteCount, streamHandle);
317    }
318
319    private native void setInputImpl(byte[] buf, int offset, int byteCount, long handle);
320
321    synchronized int setFileInput(FileDescriptor fd, long offset, int byteCount) {
322        checkOpen();
323        inRead = 0;
324        inLength = setFileInputImpl(fd, offset, byteCount, streamHandle);
325        return inLength;
326    }
327
328    private native int setFileInputImpl(FileDescriptor fd, long offset, int byteCount, long handle);
329
330    private void checkOpen() {
331        if (streamHandle == -1) {
332            throw new IllegalStateException("attempt to use Inflater after calling end");
333        }
334    }
335}
336