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 total number of bytes of input read by this {@code Inflater}. This
174     * method is limited to 32 bits; use {@link #getBytesRead} instead.
175     */
176    public synchronized int getTotalIn() {
177        checkOpen();
178        return (int) Math.min(getTotalInImpl(streamHandle), (long) Integer.MAX_VALUE);
179    }
180
181    private native long getTotalInImpl(long handle);
182
183    /**
184     * Returns the total number of bytes written to the output buffer by this {@code
185     * Inflater}. The method is limited to 32 bits; use {@link #getBytesWritten} instead.
186     */
187    public synchronized int getTotalOut() {
188        checkOpen();
189        return (int) Math.min(getTotalOutImpl(streamHandle), (long) Integer.MAX_VALUE);
190    }
191
192    private native long getTotalOutImpl(long handle);
193
194    /**
195     * Inflates bytes from the current input and stores them in {@code buf}.
196     *
197     * @param buf
198     *            the buffer where decompressed data bytes are written.
199     * @return the number of bytes inflated.
200     * @throws DataFormatException
201     *             if the underlying stream is corrupted or was not compressed
202     *             using a {@code Deflater}.
203     */
204    public int inflate(byte[] buf) throws DataFormatException {
205        return inflate(buf, 0, buf.length);
206    }
207
208    /**
209     * Inflates up to {@code byteCount} bytes from the current input and stores them in
210     * {@code buf} starting at {@code offset}.
211     *
212     * @throws DataFormatException
213     *             if the underlying stream is corrupted or was not compressed
214     *             using a {@code Deflater}.
215     * @return the number of bytes inflated.
216     */
217    public synchronized int inflate(byte[] buf, int offset, int byteCount) throws DataFormatException {
218        Arrays.checkOffsetAndCount(buf.length, offset, byteCount);
219
220        checkOpen();
221
222        if (needsInput()) {
223            return 0;
224        }
225
226        boolean neededDict = needsDictionary;
227        needsDictionary = false;
228        int result = inflateImpl(buf, offset, byteCount, streamHandle);
229        if (needsDictionary && neededDict) {
230            throw new DataFormatException("Needs dictionary");
231        }
232        return result;
233    }
234
235    private native int inflateImpl(byte[] buf, int offset, int byteCount, long handle);
236
237    /**
238     * Returns true if the input bytes were compressed with a preset
239     * dictionary. This method should be called if the first call to {@link #inflate} returns 0,
240     * to determine whether a dictionary is required. If so, {@link #setDictionary}
241     * should be called with the appropriate dictionary before calling {@code
242     * inflate} again. Use {@link #getAdler} to determine which dictionary is required.
243     */
244    public synchronized boolean needsDictionary() {
245        return needsDictionary;
246    }
247
248    /**
249     * Returns true if {@link #setInput} must be called before inflation can continue.
250     */
251    public synchronized boolean needsInput() {
252        return inRead == inLength;
253    }
254
255    /**
256     * Resets this {@code Inflater}. Should be called prior to inflating a new
257     * set of data.
258     */
259    public synchronized void reset() {
260        checkOpen();
261        finished = false;
262        needsDictionary = false;
263        inLength = inRead = 0;
264        resetImpl(streamHandle);
265    }
266
267    private native void resetImpl(long handle);
268
269    /**
270     * Sets the preset dictionary to be used for inflation to {@code dictionary}.
271     * See {@link #needsDictionary} for details.
272     */
273    public synchronized void setDictionary(byte[] dictionary) {
274        setDictionary(dictionary, 0, dictionary.length);
275    }
276
277    /**
278     * Sets the preset dictionary to be used for inflation to a subsequence of {@code dictionary}
279     * starting at {@code offset} and continuing for {@code byteCount} bytes. See {@link
280     * #needsDictionary} for details.
281     */
282    public synchronized void setDictionary(byte[] dictionary, int offset, int byteCount) {
283        checkOpen();
284        Arrays.checkOffsetAndCount(dictionary.length, offset, byteCount);
285        setDictionaryImpl(dictionary, offset, byteCount, streamHandle);
286    }
287
288    private native void setDictionaryImpl(byte[] dictionary, int offset, int byteCount, long handle);
289
290    /**
291     * Sets the current input to to be decompressed. This method should only be
292     * called if {@link #needsInput} returns {@code true}.
293     */
294    public synchronized void setInput(byte[] buf) {
295        setInput(buf, 0, buf.length);
296    }
297
298    /**
299     * Sets the current input to to be decompressed. This method should only be
300     * called if {@link #needsInput} returns {@code true}.
301     */
302    public synchronized void setInput(byte[] buf, int offset, int byteCount) {
303        checkOpen();
304        Arrays.checkOffsetAndCount(buf.length, offset, byteCount);
305        inRead = 0;
306        inLength = byteCount;
307        setInputImpl(buf, offset, byteCount, streamHandle);
308    }
309
310    private native void setInputImpl(byte[] buf, int offset, int byteCount, long handle);
311
312    synchronized int setFileInput(FileDescriptor fd, long offset, int byteCount) {
313        checkOpen();
314        inRead = 0;
315        inLength = setFileInputImpl(fd, offset, byteCount, streamHandle);
316        return inLength;
317    }
318
319    private native int setFileInputImpl(FileDescriptor fd, long offset, int byteCount, long handle);
320
321    private void checkOpen() {
322        if (streamHandle == -1) {
323            throw new IllegalStateException("attempt to use Inflater after calling end");
324        }
325    }
326}
327