• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
18 package java.util.zip;
19 
20 import dalvik.system.CloseGuard;
21 import java.io.FileDescriptor;
22 import 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  */
51 public 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      */
Inflater()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      */
Inflater(boolean noHeader)80     public Inflater(boolean noHeader) {
81         streamHandle = createStream(noHeader);
82         guard.open("end");
83     }
84 
createStream(boolean noHeader1)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      */
end()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 
endImpl(long handle)103     private native void endImpl(long handle);
104 
finalize()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      */
finished()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      */
getAdler()137     public synchronized int getAdler() {
138         checkOpen();
139         return getAdlerImpl(streamHandle);
140     }
141 
getAdlerImpl(long handle)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      */
getBytesRead()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      */
getBytesWritten()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      */
getRemaining()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      */
getTotalIn()176     public synchronized int getTotalIn() {
177         checkOpen();
178         return (int) Math.min(getTotalInImpl(streamHandle), (long) Integer.MAX_VALUE);
179     }
180 
getTotalInImpl(long handle)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      */
getTotalOut()187     public synchronized int getTotalOut() {
188         checkOpen();
189         return (int) Math.min(getTotalOutImpl(streamHandle), (long) Integer.MAX_VALUE);
190     }
191 
getTotalOutImpl(long handle)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      */
inflate(byte[] buf)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      */
inflate(byte[] buf, int offset, int byteCount)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 
inflateImpl(byte[] buf, int offset, int byteCount, long handle)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      */
needsDictionary()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      */
needsInput()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      */
reset()259     public synchronized void reset() {
260         checkOpen();
261         finished = false;
262         needsDictionary = false;
263         inLength = inRead = 0;
264         resetImpl(streamHandle);
265     }
266 
resetImpl(long handle)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      */
setDictionary(byte[] dictionary)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      */
setDictionary(byte[] dictionary, int offset, int byteCount)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 
setDictionaryImpl(byte[] dictionary, int offset, int byteCount, long handle)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      */
setInput(byte[] buf)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      */
setInput(byte[] buf, int offset, int byteCount)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 
setInputImpl(byte[] buf, int offset, int byteCount, long handle)310     private native void setInputImpl(byte[] buf, int offset, int byteCount, long handle);
311 
setFileInput(FileDescriptor fd, long offset, int byteCount)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 
setFileInputImpl(FileDescriptor fd, long offset, int byteCount, long handle)319     private native int setFileInputImpl(FileDescriptor fd, long offset, int byteCount, long handle);
320 
checkOpen()321     private void checkOpen() {
322         if (streamHandle == -1) {
323             throw new IllegalStateException("attempt to use Inflater after calling end");
324         }
325     }
326 }
327