1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This code is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License version 2 only, as
8  * published by the Free Software Foundation.  Oracle designates this
9  * particular file as subject to the "Classpath" exception as provided
10  * by Oracle in the LICENSE file that accompanied this code.
11  *
12  * This code is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15  * version 2 for more details (a copy is included in the LICENSE file that
16  * accompanied this code).
17  *
18  * You should have received a copy of the GNU General Public License version
19  * 2 along with this work; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21  *
22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23  * or visit www.oracle.com if you need additional information or have any
24  * questions.
25  */
26 
27 package java.util.zip;
28 
29 import dalvik.annotation.optimization.ReachabilitySensitive;
30 import dalvik.system.CloseGuard;
31 import java.lang.ref.Cleaner.Cleanable;
32 import java.lang.ref.Reference;
33 import java.nio.ByteBuffer;
34 import java.nio.ReadOnlyBufferException;
35 import java.util.Objects;
36 
37 import jdk.internal.ref.CleanerFactory;
38 import sun.nio.ch.DirectBuffer;
39 
40 /**
41  * This class provides support for general purpose decompression using the
42  * popular ZLIB compression library. The ZLIB compression library was
43  * initially developed as part of the PNG graphics standard and is not
44  * protected by patents. It is fully described in the specifications at
45  * the <a href="package-summary.html#package-description">java.util.zip
46  * package description</a>.
47  * <p>
48  * This class inflates sequences of ZLIB compressed bytes. The input byte
49  * sequence is provided in either byte array or byte buffer, via one of the
50  * {@code setInput()} methods. The output byte sequence is written to the
51  * output byte array or byte buffer passed to the {@code deflate()} methods.
52  * <p>
53  * The following code fragment demonstrates a trivial compression
54  * and decompression of a string using {@code Deflater} and
55  * {@code Inflater}.
56  *
57  * <blockquote><pre>
58  * try {
59  *     // Encode a String into bytes
60  *     String inputString = "blahblahblah\u20AC\u20AC";
61  *     byte[] input = inputString.getBytes("UTF-8");
62  *
63  *     // Compress the bytes
64  *     byte[] output = new byte[100];
65  *     Deflater compresser = new Deflater();
66  *     compresser.setInput(input);
67  *     compresser.finish();
68  *     int compressedDataLength = compresser.deflate(output);
69  *
70  *     // Decompress the bytes
71  *     Inflater decompresser = new Inflater();
72  *     decompresser.setInput(output, 0, compressedDataLength);
73  *     byte[] result = new byte[100];
74  *     int resultLength = decompresser.inflate(result);
75  *     decompresser.end();
76  *
77  *     // Decode the bytes into a String
78  *     String outputString = new String(result, 0, resultLength, "UTF-8");
79  * } catch (java.io.UnsupportedEncodingException ex) {
80  *     // handle
81  * } catch (java.util.zip.DataFormatException ex) {
82  *     // handle
83  * }
84  * </pre></blockquote>
85  *
86  * @apiNote
87  * To release resources used by this {@code Inflater}, the {@link #end()} method
88  * should be called explicitly. Subclasses are responsible for the cleanup of resources
89  * acquired by the subclass. Subclasses that override {@link #finalize()} in order
90  * to perform cleanup should be modified to use alternative cleanup mechanisms such
91  * as {@link java.lang.ref.Cleaner} and remove the overriding {@code finalize} method.
92  *
93  * @see         Deflater
94  * @author      David Connelly
95  * @since 1.1
96  *
97  */
98 
99 public class Inflater {
100 
101     private final InflaterZStreamRef zsRef;
102     private ByteBuffer input = ZipUtils.defaultBuf;
103     private byte[] inputArray;
104     private int inputPos, inputLim;
105     private boolean finished;
106     private boolean needDict;
107     private long bytesRead;
108     private long bytesWritten;
109 
110     // Android-added: CloseGuard support.
111     @ReachabilitySensitive
112     private final CloseGuard guard = CloseGuard.get();
113 
114     /*
115      * These fields are used as an "out" parameter from JNI when a
116      * DataFormatException is thrown during the inflate operation.
117      */
118     private int inputConsumed;
119     private int outputConsumed;
120 
121     // Android-removed: initIDs handled in register method.
122     /*
123     static {
124         ZipUtils.loadLibrary();
125         initIDs();
126     }
127     */
128 
129     /**
130      * Creates a new decompressor. If the parameter 'nowrap' is true then
131      * the ZLIB header and checksum fields will not be used. This provides
132      * compatibility with the compression format used by both GZIP and PKZIP.
133      * <p>
134      * Note: When using the 'nowrap' option it is also necessary to provide
135      * an extra "dummy" byte as input. This is required by the ZLIB native
136      * library in order to support certain optimizations.
137      *
138      * @param nowrap if true then support GZIP compatible compression
139      */
Inflater(boolean nowrap)140     public Inflater(boolean nowrap) {
141         this.zsRef = new InflaterZStreamRef(this, init(nowrap));
142         // Android-added: CloseGuard support.
143         guard.open("end");
144     }
145 
146     /**
147      * Creates a new decompressor.
148      */
Inflater()149     public Inflater() {
150         this(false);
151     }
152 
153     /**
154      * Sets input data for decompression.
155      * <p>
156      * One of the {@code setInput()} methods should be called whenever
157      * {@code needsInput()} returns true indicating that more input data
158      * is required.
159      *
160      * @param input the input data bytes
161      * @param off the start offset of the input data
162      * @param len the length of the input data
163      * @see Inflater#needsInput
164      */
setInput(byte[] input, int off, int len)165     public void setInput(byte[] input, int off, int len) {
166         if (off < 0 || len < 0 || off > input.length - len) {
167             throw new ArrayIndexOutOfBoundsException();
168         }
169         synchronized (zsRef) {
170             this.input = null;
171             this.inputArray = input;
172             this.inputPos = off;
173             this.inputLim = off + len;
174         }
175     }
176 
177     /**
178      * Sets input data for decompression.
179      * <p>
180      * One of the {@code setInput()} methods should be called whenever
181      * {@code needsInput()} returns true indicating that more input data
182      * is required.
183      *
184      * @param input the input data bytes
185      * @see Inflater#needsInput
186      */
setInput(byte[] input)187     public void setInput(byte[] input) {
188         setInput(input, 0, input.length);
189     }
190 
191     /**
192      * Sets input data for decompression.
193      * <p>
194      * One of the {@code setInput()} methods should be called whenever
195      * {@code needsInput()} returns true indicating that more input data
196      * is required.
197      * <p>
198      * The given buffer's position will be advanced as inflate
199      * operations are performed, up to the buffer's limit.
200      * The input buffer may be modified (refilled) between inflate
201      * operations; doing so is equivalent to creating a new buffer
202      * and setting it with this method.
203      * <p>
204      * Modifying the input buffer's contents, position, or limit
205      * concurrently with an inflate operation will result in
206      * undefined behavior, which may include incorrect operation
207      * results or operation failure.
208      *
209      * @param input the input data bytes
210      * @see Inflater#needsInput
211      * @since 11
212      */
setInput(ByteBuffer input)213     public void setInput(ByteBuffer input) {
214         Objects.requireNonNull(input);
215         synchronized (zsRef) {
216             this.input = input;
217             this.inputArray = null;
218         }
219     }
220 
221     /**
222      * Sets the preset dictionary to the given array of bytes. Should be
223      * called when inflate() returns 0 and needsDictionary() returns true
224      * indicating that a preset dictionary is required. The method getAdler()
225      * can be used to get the Adler-32 value of the dictionary needed.
226      * @param dictionary the dictionary data bytes
227      * @param off the start offset of the data
228      * @param len the length of the data
229      * @see Inflater#needsDictionary
230      * @see Inflater#getAdler
231      */
setDictionary(byte[] dictionary, int off, int len)232     public void setDictionary(byte[] dictionary, int off, int len) {
233         if (off < 0 || len < 0 || off > dictionary.length - len) {
234             throw new ArrayIndexOutOfBoundsException();
235         }
236         synchronized (zsRef) {
237             ensureOpen();
238             setDictionary(zsRef.address(), dictionary, off, len);
239             needDict = false;
240         }
241     }
242 
243     /**
244      * Sets the preset dictionary to the given array of bytes. Should be
245      * called when inflate() returns 0 and needsDictionary() returns true
246      * indicating that a preset dictionary is required. The method getAdler()
247      * can be used to get the Adler-32 value of the dictionary needed.
248      * @param dictionary the dictionary data bytes
249      * @see Inflater#needsDictionary
250      * @see Inflater#getAdler
251      */
setDictionary(byte[] dictionary)252     public void setDictionary(byte[] dictionary) {
253         setDictionary(dictionary, 0, dictionary.length);
254     }
255 
256     /**
257      * Sets the preset dictionary to the bytes in the given buffer. Should be
258      * called when inflate() returns 0 and needsDictionary() returns true
259      * indicating that a preset dictionary is required. The method getAdler()
260      * can be used to get the Adler-32 value of the dictionary needed.
261      * <p>
262      * The bytes in given byte buffer will be fully consumed by this method.  On
263      * return, its position will equal its limit.
264      *
265      * @param dictionary the dictionary data bytes
266      * @see Inflater#needsDictionary
267      * @see Inflater#getAdler
268      * @since 11
269      */
setDictionary(ByteBuffer dictionary)270     public void setDictionary(ByteBuffer dictionary) {
271         synchronized (zsRef) {
272             int position = dictionary.position();
273             int remaining = Math.max(dictionary.limit() - position, 0);
274             ensureOpen();
275             if (dictionary.isDirect()) {
276                 long address = ((DirectBuffer) dictionary).address();
277                 try {
278                     setDictionaryBuffer(zsRef.address(), address + position, remaining);
279                 } finally {
280                     Reference.reachabilityFence(dictionary);
281                 }
282             } else {
283                 byte[] array = ZipUtils.getBufferArray(dictionary);
284                 int offset = ZipUtils.getBufferOffset(dictionary);
285                 setDictionary(zsRef.address(), array, offset + position, remaining);
286             }
287             dictionary.position(position + remaining);
288             needDict = false;
289         }
290     }
291 
292     /**
293      * Returns the total number of bytes remaining in the input buffer.
294      * This can be used to find out what bytes still remain in the input
295      * buffer after decompression has finished.
296      * @return the total number of bytes remaining in the input buffer
297      */
getRemaining()298     public int getRemaining() {
299         synchronized (zsRef) {
300             ByteBuffer input = this.input;
301             return input == null ? inputLim - inputPos : input.remaining();
302         }
303     }
304 
305     /**
306      * Returns true if no data remains in the input buffer. This can
307      * be used to determine if one of the {@code setInput()} methods should be
308      * called in order to provide more input.
309      *
310      * @return true if no data remains in the input buffer
311      */
needsInput()312     public boolean needsInput() {
313         synchronized (zsRef) {
314             ByteBuffer input = this.input;
315             return input == null ? inputLim == inputPos : ! input.hasRemaining();
316         }
317     }
318 
319     /**
320      * Returns true if a preset dictionary is needed for decompression.
321      * @return true if a preset dictionary is needed for decompression
322      * @see Inflater#setDictionary
323      */
needsDictionary()324     public boolean needsDictionary() {
325         synchronized (zsRef) {
326             return needDict;
327         }
328     }
329 
330     /**
331      * Returns true if the end of the compressed data stream has been
332      * reached.
333      * @return true if the end of the compressed data stream has been
334      * reached
335      */
finished()336     public boolean finished() {
337         synchronized (zsRef) {
338             return finished;
339         }
340     }
341 
342     /**
343      * Uncompresses bytes into specified buffer. Returns actual number
344      * of bytes uncompressed. A return value of 0 indicates that
345      * needsInput() or needsDictionary() should be called in order to
346      * determine if more input data or a preset dictionary is required.
347      * In the latter case, getAdler() can be used to get the Adler-32
348      * value of the dictionary required.
349      * <p>
350      * If the {@link #setInput(ByteBuffer)} method was called to provide a buffer
351      * for input, the input buffer's position will be advanced by the number of bytes
352      * consumed by this operation, even in the event that a {@link DataFormatException}
353      * is thrown.
354      * <p>
355      * The {@linkplain #getRemaining() remaining byte count} will be reduced by
356      * the number of consumed input bytes.  If the {@link #setInput(ByteBuffer)}
357      * method was called to provide a buffer for input, the input buffer's position
358      * will be advanced the number of consumed bytes.
359      * <p>
360      * These byte totals, as well as
361      * the {@linkplain #getBytesRead() total bytes read}
362      * and the {@linkplain #getBytesWritten() total bytes written}
363      * values, will be updated even in the event that a {@link DataFormatException}
364      * is thrown to reflect the amount of data consumed and produced before the
365      * exception occurred.
366      *
367      * @param output the buffer for the uncompressed data
368      * @param off the start offset of the data
369      * @param len the maximum number of uncompressed bytes
370      * @return the actual number of uncompressed bytes
371      * @throws DataFormatException if the compressed data format is invalid
372      * @see Inflater#needsInput
373      * @see Inflater#needsDictionary
374      */
inflate(byte[] output, int off, int len)375     public int inflate(byte[] output, int off, int len)
376         throws DataFormatException
377     {
378         if (off < 0 || len < 0 || off > output.length - len) {
379             throw new ArrayIndexOutOfBoundsException();
380         }
381         synchronized (zsRef) {
382             ensureOpen();
383             ByteBuffer input = this.input;
384             long result;
385             int inputPos;
386             try {
387                 if (input == null) {
388                     inputPos = this.inputPos;
389                     try {
390                         result = inflateBytesBytes(zsRef.address(),
391                             inputArray, inputPos, inputLim - inputPos,
392                             output, off, len);
393                     } catch (DataFormatException e) {
394                         this.inputPos = inputPos + inputConsumed;
395                         throw e;
396                     }
397                 } else {
398                     inputPos = input.position();
399                     try {
400                         int inputRem = Math.max(input.limit() - inputPos, 0);
401                         if (input.isDirect()) {
402                             try {
403                                 long inputAddress = ((DirectBuffer) input).address();
404                                 result = inflateBufferBytes(zsRef.address(),
405                                     inputAddress + inputPos, inputRem,
406                                     output, off, len);
407                             } finally {
408                                 Reference.reachabilityFence(input);
409                             }
410                         } else {
411                             byte[] inputArray = ZipUtils.getBufferArray(input);
412                             int inputOffset = ZipUtils.getBufferOffset(input);
413                             result = inflateBytesBytes(zsRef.address(),
414                                 inputArray, inputOffset + inputPos, inputRem,
415                                 output, off, len);
416                         }
417                     } catch (DataFormatException e) {
418                         input.position(inputPos + inputConsumed);
419                         throw e;
420                     }
421                 }
422             } catch (DataFormatException e) {
423                 bytesRead += inputConsumed;
424                 inputConsumed = 0;
425                 int written = outputConsumed;
426                 bytesWritten += written;
427                 outputConsumed = 0;
428                 throw e;
429             }
430             int read = (int) (result & 0x7fff_ffffL);
431             int written = (int) (result >>> 31 & 0x7fff_ffffL);
432             if ((result >>> 62 & 1) != 0) {
433                 finished = true;
434             }
435             if ((result >>> 63 & 1) != 0) {
436                 needDict = true;
437             }
438             if (input != null) {
439                 input.position(inputPos + read);
440             } else {
441                 this.inputPos = inputPos + read;
442             }
443             bytesWritten += written;
444             bytesRead += read;
445             return written;
446         }
447     }
448 
449     /**
450      * Uncompresses bytes into specified buffer. Returns actual number
451      * of bytes uncompressed. A return value of 0 indicates that
452      * needsInput() or needsDictionary() should be called in order to
453      * determine if more input data or a preset dictionary is required.
454      * In the latter case, getAdler() can be used to get the Adler-32
455      * value of the dictionary required.
456      * <p>
457      * The {@linkplain #getRemaining() remaining byte count} will be reduced by
458      * the number of consumed input bytes.  If the {@link #setInput(ByteBuffer)}
459      * method was called to provide a buffer for input, the input buffer's position
460      * will be advanced the number of consumed bytes.
461      * <p>
462      * These byte totals, as well as
463      * the {@linkplain #getBytesRead() total bytes read}
464      * and the {@linkplain #getBytesWritten() total bytes written}
465      * values, will be updated even in the event that a {@link DataFormatException}
466      * is thrown to reflect the amount of data consumed and produced before the
467      * exception occurred.
468      *
469      * @param output the buffer for the uncompressed data
470      * @return the actual number of uncompressed bytes
471      * @throws DataFormatException if the compressed data format is invalid
472      * @see Inflater#needsInput
473      * @see Inflater#needsDictionary
474      */
inflate(byte[] output)475     public int inflate(byte[] output) throws DataFormatException {
476         return inflate(output, 0, output.length);
477     }
478 
479     /**
480      * Uncompresses bytes into specified buffer. Returns actual number
481      * of bytes uncompressed. A return value of 0 indicates that
482      * needsInput() or needsDictionary() should be called in order to
483      * determine if more input data or a preset dictionary is required.
484      * In the latter case, getAdler() can be used to get the Adler-32
485      * value of the dictionary required.
486      * <p>
487      * On success, the position of the given {@code output} byte buffer will be
488      * advanced by as many bytes as were produced by the operation, which is equal
489      * to the number returned by this method.  Note that the position of the
490      * {@code output} buffer will be advanced even in the event that a
491      * {@link DataFormatException} is thrown.
492      * <p>
493      * The {@linkplain #getRemaining() remaining byte count} will be reduced by
494      * the number of consumed input bytes.  If the {@link #setInput(ByteBuffer)}
495      * method was called to provide a buffer for input, the input buffer's position
496      * will be advanced the number of consumed bytes.
497      * <p>
498      * These byte totals, as well as
499      * the {@linkplain #getBytesRead() total bytes read}
500      * and the {@linkplain #getBytesWritten() total bytes written}
501      * values, will be updated even in the event that a {@link DataFormatException}
502      * is thrown to reflect the amount of data consumed and produced before the
503      * exception occurred.
504      *
505      * @param output the buffer for the uncompressed data
506      * @return the actual number of uncompressed bytes
507      * @throws DataFormatException if the compressed data format is invalid
508      * @throws ReadOnlyBufferException if the given output buffer is read-only
509      * @see Inflater#needsInput
510      * @see Inflater#needsDictionary
511      * @since 11
512      */
inflate(ByteBuffer output)513     public int inflate(ByteBuffer output) throws DataFormatException {
514         if (output.isReadOnly()) {
515             throw new ReadOnlyBufferException();
516         }
517         synchronized (zsRef) {
518             ensureOpen();
519             ByteBuffer input = this.input;
520             long result;
521             int inputPos;
522             int outputPos = output.position();
523             int outputRem = Math.max(output.limit() - outputPos, 0);
524             try {
525                 if (input == null) {
526                     inputPos = this.inputPos;
527                     try {
528                         if (output.isDirect()) {
529                             long outputAddress = ((DirectBuffer) output).address();
530                             try {
531                                 result = inflateBytesBuffer(zsRef.address(),
532                                     inputArray, inputPos, inputLim - inputPos,
533                                     outputAddress + outputPos, outputRem);
534                             } finally {
535                                 Reference.reachabilityFence(output);
536                             }
537                         } else {
538                             byte[] outputArray = ZipUtils.getBufferArray(output);
539                             int outputOffset = ZipUtils.getBufferOffset(output);
540                             result = inflateBytesBytes(zsRef.address(),
541                                 inputArray, inputPos, inputLim - inputPos,
542                                 outputArray, outputOffset + outputPos, outputRem);
543                         }
544                     } catch (DataFormatException e) {
545                         this.inputPos = inputPos + inputConsumed;
546                         throw e;
547                     }
548                 } else {
549                     inputPos = input.position();
550                     int inputRem = Math.max(input.limit() - inputPos, 0);
551                     try {
552                         if (input.isDirect()) {
553                             long inputAddress = ((DirectBuffer) input).address();
554                             try {
555                                 if (output.isDirect()) {
556                                     long outputAddress = ((DirectBuffer) output).address();
557                                     try {
558                                         result = inflateBufferBuffer(zsRef.address(),
559                                             inputAddress + inputPos, inputRem,
560                                             outputAddress + outputPos, outputRem);
561                                     } finally {
562                                         Reference.reachabilityFence(output);
563                                     }
564                                 } else {
565                                     byte[] outputArray = ZipUtils.getBufferArray(output);
566                                     int outputOffset = ZipUtils.getBufferOffset(output);
567                                     result = inflateBufferBytes(zsRef.address(),
568                                         inputAddress + inputPos, inputRem,
569                                         outputArray, outputOffset + outputPos, outputRem);
570                                 }
571                             } finally {
572                                 Reference.reachabilityFence(input);
573                             }
574                         } else {
575                             byte[] inputArray = ZipUtils.getBufferArray(input);
576                             int inputOffset = ZipUtils.getBufferOffset(input);
577                             if (output.isDirect()) {
578                                 long outputAddress = ((DirectBuffer) output).address();
579                                 try {
580                                     result = inflateBytesBuffer(zsRef.address(),
581                                         inputArray, inputOffset + inputPos, inputRem,
582                                         outputAddress + outputPos, outputRem);
583                                 } finally {
584                                     Reference.reachabilityFence(output);
585                                 }
586                             } else {
587                                 byte[] outputArray = ZipUtils.getBufferArray(output);
588                                 int outputOffset = ZipUtils.getBufferOffset(output);
589                                 result = inflateBytesBytes(zsRef.address(),
590                                     inputArray, inputOffset + inputPos, inputRem,
591                                     outputArray, outputOffset + outputPos, outputRem);
592                             }
593                         }
594                     } catch (DataFormatException e) {
595                         input.position(inputPos + inputConsumed);
596                         throw e;
597                     }
598                 }
599             } catch (DataFormatException e) {
600                 bytesRead += inputConsumed;
601                 inputConsumed = 0;
602                 int written = outputConsumed;
603                 output.position(outputPos + written);
604                 bytesWritten += written;
605                 outputConsumed = 0;
606                 throw e;
607             }
608             int read = (int) (result & 0x7fff_ffffL);
609             int written = (int) (result >>> 31 & 0x7fff_ffffL);
610             if ((result >>> 62 & 1) != 0) {
611                 finished = true;
612             }
613             if ((result >>> 63 & 1) != 0) {
614                 needDict = true;
615             }
616             if (input != null) {
617                 input.position(inputPos + read);
618             } else {
619                 this.inputPos = inputPos + read;
620             }
621             // Note: this method call also serves to keep the byteBuffer ref alive
622             output.position(outputPos + written);
623             bytesWritten += written;
624             bytesRead += read;
625             return written;
626         }
627     }
628 
629     /**
630      * Returns the ADLER-32 value of the uncompressed data.
631      * @return the ADLER-32 value of the uncompressed data
632      */
getAdler()633     public int getAdler() {
634         synchronized (zsRef) {
635             ensureOpen();
636             return getAdler(zsRef.address());
637         }
638     }
639 
640     /**
641      * Returns the total number of compressed bytes input so far.
642      *
643      * <p>Since the number of bytes may be greater than
644      * Integer.MAX_VALUE, the {@link #getBytesRead()} method is now
645      * the preferred means of obtaining this information.</p>
646      *
647      * @return the total number of compressed bytes input so far
648      */
getTotalIn()649     public int getTotalIn() {
650         return (int) getBytesRead();
651     }
652 
653     /**
654      * Returns the total number of compressed bytes input so far.
655      *
656      * @return the total (non-negative) number of compressed bytes input so far
657      * @since 1.5
658      */
getBytesRead()659     public long getBytesRead() {
660         synchronized (zsRef) {
661             ensureOpen();
662             return bytesRead;
663         }
664     }
665 
666     /**
667      * Returns the total number of uncompressed bytes output so far.
668      *
669      * <p>Since the number of bytes may be greater than
670      * Integer.MAX_VALUE, the {@link #getBytesWritten()} method is now
671      * the preferred means of obtaining this information.</p>
672      *
673      * @return the total number of uncompressed bytes output so far
674      */
getTotalOut()675     public int getTotalOut() {
676         return (int) getBytesWritten();
677     }
678 
679     /**
680      * Returns the total number of uncompressed bytes output so far.
681      *
682      * @return the total (non-negative) number of uncompressed bytes output so far
683      * @since 1.5
684      */
getBytesWritten()685     public long getBytesWritten() {
686         synchronized (zsRef) {
687             ensureOpen();
688             return bytesWritten;
689         }
690     }
691 
692     /**
693      * Resets inflater so that a new set of input data can be processed.
694      */
reset()695     public void reset() {
696         synchronized (zsRef) {
697             ensureOpen();
698             reset(zsRef.address());
699             // Android-added: CloseGuard support.
700             guard.close();
701             input = ZipUtils.defaultBuf;
702             inputArray = null;
703             finished = false;
704             needDict = false;
705             bytesRead = bytesWritten = 0;
706         }
707     }
708 
709     /**
710      * Closes the decompressor and discards any unprocessed input.
711      *
712      * This method should be called when the decompressor is no longer
713      * being used. Once this method is called, the behavior of the
714      * Inflater object is undefined.
715      */
end()716     public void end() {
717         synchronized (zsRef) {
718             zsRef.clean();
719             // Android-added: CloseGuard support.
720             guard.close();
721             input = ZipUtils.defaultBuf;
722             inputArray = null;
723         }
724     }
725 
726 
ensureOpen()727     private void ensureOpen () {
728         assert Thread.holdsLock(zsRef);
729         if (zsRef.address() == 0)
730             throw new NullPointerException("Inflater has been closed");
731     }
732 
733     // Android-changed: initIDs handled in register method.
734     // private native static void initIDs();
init(boolean nowrap)735     private static native long init(boolean nowrap);
setDictionary(long addr, byte[] b, int off, int len)736     private static native void setDictionary(long addr, byte[] b, int off,
737                                              int len);
setDictionaryBuffer(long addr, long bufAddress, int len)738     private static native void setDictionaryBuffer(long addr, long bufAddress, int len);
inflateBytesBytes(long addr, byte[] inputArray, int inputOff, int inputLen, byte[] outputArray, int outputOff, int outputLen)739     private native long inflateBytesBytes(long addr,
740         byte[] inputArray, int inputOff, int inputLen,
741         byte[] outputArray, int outputOff, int outputLen) throws DataFormatException;
inflateBytesBuffer(long addr, byte[] inputArray, int inputOff, int inputLen, long outputAddress, int outputLen)742     private native long inflateBytesBuffer(long addr,
743         byte[] inputArray, int inputOff, int inputLen,
744         long outputAddress, int outputLen) throws DataFormatException;
inflateBufferBytes(long addr, long inputAddress, int inputLen, byte[] outputArray, int outputOff, int outputLen)745     private native long inflateBufferBytes(long addr,
746         long inputAddress, int inputLen,
747         byte[] outputArray, int outputOff, int outputLen) throws DataFormatException;
inflateBufferBuffer(long addr, long inputAddress, int inputLen, long outputAddress, int outputLen)748     private native long inflateBufferBuffer(long addr,
749         long inputAddress, int inputLen,
750         long outputAddress, int outputLen) throws DataFormatException;
getAdler(long addr)751     private static native int getAdler(long addr);
reset(long addr)752     private static native void reset(long addr);
end(long addr)753     private static native void end(long addr);
754 
755     /**
756      * A reference to the native zlib's z_stream structure. It also
757      * serves as the "cleaner" to clean up the native resource when
758      * the Inflater is ended, closed or cleaned.
759      */
760     static class InflaterZStreamRef implements Runnable {
761 
762         private long address;
763         private final Cleanable cleanable;
764 
InflaterZStreamRef(Inflater owner, long addr)765         private InflaterZStreamRef(Inflater owner, long addr) {
766             this.cleanable = (owner != null) ? CleanerFactory.cleaner().register(owner, this) : null;
767             this.address = addr;
768         }
769 
address()770         long address() {
771             return address;
772         }
773 
clean()774         void clean() {
775             cleanable.clean();
776         }
777 
run()778         public synchronized void run() {
779             long addr = address;
780             address = 0;
781             if (addr != 0) {
782                 end(addr);
783             }
784         }
785 
786     }
787 }
788