1 /*
2  * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */

25
26 package java.util.zip;
27
28 import java.lang.ref.Cleaner.Cleanable;
29 import java.lang.ref.Reference;
30 import java.nio.ByteBuffer;
31 import java.nio.ReadOnlyBufferException;
32 import java.util.Objects;
33
34 import jdk.internal.ref.CleanerFactory;
35 import sun.nio.ch.DirectBuffer;
36
37 /**
38  * This class provides support for general purpose decompression using the
39  * popular ZLIB compression library. The ZLIB compression library was
40  * initially developed as part of the PNG graphics standard and is not
41  * protected by patents. It is fully described in the specifications at
42  * the <a href="package-summary.html#package.description">java.util.zip
43  * package description</a>.
44  * <p>
45  * This class inflates sequences of ZLIB compressed bytes. The input byte
46  * sequence is provided in either byte array or byte buffer, via one of the
47  * {@code setInput()} methods. The output byte sequence is written to the
48  * output byte array or byte buffer passed to the {@code deflate()} methods.
49  * <p>
50  * The following code fragment demonstrates a trivial compression
51  * and decompression of a string using {@code Deflater} and
52  * {@code Inflater}.
53  *
54  * <blockquote><pre>
55  * try {
56  *     // Encode a String into bytes
57  *     String inputString = "blahblahblah\u20AC\u20AC";
58  *     byte[] input = inputString.getBytes("UTF-8");
59  *
60  *     // Compress the bytes
61  *     byte[] output = new byte[100];
62  *     Deflater compresser = new Deflater();
63  *     compresser.setInput(input);
64  *     compresser.finish();
65  *     int compressedDataLength = compresser.deflate(output);
66  *
67  *     // Decompress the bytes
68  *     Inflater decompresser = new Inflater();
69  *     decompresser.setInput(output, 0, compressedDataLength);
70  *     byte[] result = new byte[100];
71  *     int resultLength = decompresser.inflate(result);
72  *     decompresser.end();
73  *
74  *     // Decode the bytes into a String
75  *     String outputString = new String(result, 0, resultLength, "UTF-8");
76  * } catch (java.io.UnsupportedEncodingException ex) {
77  *     // handle
78  * } catch (java.util.zip.DataFormatException ex) {
79  *     // handle
80  * }
81  * </pre></blockquote>
82  *
83  * @apiNote
84  * To release resources used by this {@code Inflater}, the {@link #end()} method
85  * should be called explicitly. Subclasses are responsible for the cleanup of resources
86  * acquired by the subclass. Subclasses that override {@link #finalize()} in order
87  * to perform cleanup should be modified to use alternative cleanup mechanisms such
88  * as {@link java.lang.ref.Cleaner} and remove the overriding {@code finalize} method.
89  *
90  * @implSpec
91  * If this {@code Inflater} has been subclassed and the {@code end} method has been
92  * overridden, the {@code end} method will be called by the finalization when the
93  * inflater is unreachable. But the subclasses should not depend on this specific
94  * implementation; the finalization is not reliable and the {@code finalize} method
95  * is deprecated to be removed.
96  *
97  * @see         Deflater
98  * @author      David Connelly
99  * @since 1.1
100  *
101  */

102
103 public class Inflater {
104
105     private final InflaterZStreamRef zsRef;
106     private ByteBuffer input = ZipUtils.defaultBuf;
107     private byte[] inputArray;
108     private int inputPos, inputLim;
109     private boolean finished;
110     private boolean needDict;
111     private long bytesRead;
112     private long bytesWritten;
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     static {
122         ZipUtils.loadLibrary();
123         initIDs();
124     }
125
126     /**
127      * Creates a new decompressor. If the parameter 'nowrap' is true then
128      * the ZLIB header and checksum fields will not be used. This provides
129      * compatibility with the compression format used by both GZIP and PKZIP.
130      * <p>
131      * Note: When using the 'nowrap' option it is also necessary to provide
132      * an extra "dummy" byte as input. This is required by the ZLIB native
133      * library in order to support certain optimizations.
134      *
135      * @param nowrap if true then support GZIP compatible compression
136      */

137     public Inflater(boolean nowrap) {
138         this.zsRef = InflaterZStreamRef.get(this, init(nowrap));
139     }
140
141     /**
142      * Creates a new decompressor.
143      */

144     public Inflater() {
145         this(false);
146     }
147
148     /**
149      * Sets input data for decompression.
150      * <p>
151      * One of the {@code setInput()} methods should be called whenever
152      * {@code needsInput()} returns true indicating that more input data
153      * is required.
154      *
155      * @param input the input data bytes
156      * @param off the start offset of the input data
157      * @param len the length of the input data
158      * @see Inflater#needsInput
159      */

160     public void setInput(byte[] input, int off, int len) {
161         if (off < 0 || len < 0 || off > input.length - len) {
162             throw new ArrayIndexOutOfBoundsException();
163         }
164         synchronized (zsRef) {
165             this.input = null;
166             this.inputArray = input;
167             this.inputPos = off;
168             this.inputLim = off + len;
169         }
170     }
171
172     /**
173      * Sets input data for decompression.
174      * <p>
175      * One of the {@code setInput()} methods should be called whenever
176      * {@code needsInput()} returns true indicating that more input data
177      * is required.
178      *
179      * @param input the input data bytes
180      * @see Inflater#needsInput
181      */

182     public void setInput(byte[] input) {
183         setInput(input, 0, input.length);
184     }
185
186     /**
187      * Sets input data for decompression.
188      * <p>
189      * One of the {@code setInput()} methods should be called whenever
190      * {@code needsInput()} returns true indicating that more input data
191      * is required.
192      * <p>
193      * The given buffer's position will be advanced as inflate
194      * operations are performed, up to the buffer's limit.
195      * The input buffer may be modified (refilled) between inflate
196      * operations; doing so is equivalent to creating a new buffer
197      * and setting it with this method.
198      * <p>
199      * Modifying the input buffer's contents, position, or limit
200      * concurrently with an inflate operation will result in
201      * undefined behavior, which may include incorrect operation
202      * results or operation failure.
203      *
204      * @param input the input data bytes
205      * @see Inflater#needsInput
206      * @since 11
207      */

208     public void setInput(ByteBuffer input) {
209         Objects.requireNonNull(input);
210         synchronized (zsRef) {
211             this.input = input;
212             this.inputArray = null;
213         }
214     }
215
216     /**
217      * Sets the preset dictionary to the given array of bytes. Should be
218      * called when inflate() returns 0 and needsDictionary() returns true
219      * indicating that a preset dictionary is required. The method getAdler()
220      * can be used to get the Adler-32 value of the dictionary needed.
221      * @param dictionary the dictionary data bytes
222      * @param off the start offset of the data
223      * @param len the length of the data
224      * @see Inflater#needsDictionary
225      * @see Inflater#getAdler
226      */

227     public void setDictionary(byte[] dictionary, int off, int len) {
228         if (off < 0 || len < 0 || off > dictionary.length - len) {
229             throw new ArrayIndexOutOfBoundsException();
230         }
231         synchronized (zsRef) {
232             ensureOpen();
233             setDictionary(zsRef.address(), dictionary, off, len);
234             needDict = false;
235         }
236     }
237
238     /**
239      * Sets the preset dictionary to the given array of bytes. Should be
240      * called when inflate() returns 0 and needsDictionary() returns true
241      * indicating that a preset dictionary is required. The method getAdler()
242      * can be used to get the Adler-32 value of the dictionary needed.
243      * @param dictionary the dictionary data bytes
244      * @see Inflater#needsDictionary
245      * @see Inflater#getAdler
246      */

247     public void setDictionary(byte[] dictionary) {
248         setDictionary(dictionary, 0, dictionary.length);
249     }
250
251     /**
252      * Sets the preset dictionary to the bytes in the given buffer. Should be
253      * called when inflate() returns 0 and needsDictionary() returns true
254      * indicating that a preset dictionary is required. The method getAdler()
255      * can be used to get the Adler-32 value of the dictionary needed.
256      * <p>
257      * The bytes in given byte buffer will be fully consumed by this method.  On
258      * return, its position will equal its limit.
259      *
260      * @param dictionary the dictionary data bytes
261      * @see Inflater#needsDictionary
262      * @see Inflater#getAdler
263      * @since 11
264      */

265     public void setDictionary(ByteBuffer dictionary) {
266         synchronized (zsRef) {
267             int position = dictionary.position();
268             int remaining = Math.max(dictionary.limit() - position, 0);
269             ensureOpen();
270             if (dictionary.isDirect()) {
271                 long address = ((DirectBuffer) dictionary).address();
272                 try {
273                     setDictionaryBuffer(zsRef.address(), address + position, remaining);
274                 } finally {
275                     Reference.reachabilityFence(dictionary);
276                 }
277             } else {
278                 byte[] array = ZipUtils.getBufferArray(dictionary);
279                 int offset = ZipUtils.getBufferOffset(dictionary);
280                 setDictionary(zsRef.address(), array, offset + position, remaining);
281             }
282             dictionary.position(position + remaining);
283             needDict = false;
284         }
285     }
286
287     /**
288      * Returns the total number of bytes remaining in the input buffer.
289      * This can be used to find out what bytes still remain in the input
290      * buffer after decompression has finished.
291      * @return the total number of bytes remaining in the input buffer
292      */

293     public int getRemaining() {
294         synchronized (zsRef) {
295             ByteBuffer input = this.input;
296             return input == null ? inputLim - inputPos : input.remaining();
297         }
298     }
299
300     /**
301      * Returns true if no data remains in the input buffer. This can
302      * be used to determine if one of the {@code setInput()} methods should be
303      * called in order to provide more input.
304      *
305      * @return true if no data remains in the input buffer
306      */

307     public boolean needsInput() {
308         synchronized (zsRef) {
309             ByteBuffer input = this.input;
310             return input == null ? inputLim == inputPos : ! input.hasRemaining();
311         }
312     }
313
314     /**
315      * Returns true if a preset dictionary is needed for decompression.
316      * @return true if a preset dictionary is needed for decompression
317      * @see Inflater#setDictionary
318      */

319     public boolean needsDictionary() {
320         synchronized (zsRef) {
321             return needDict;
322         }
323     }
324
325     /**
326      * Returns true if the end of the compressed data stream has been
327      * reached.
328      * @return true if the end of the compressed data stream has been
329      * reached
330      */

331     public boolean finished() {
332         synchronized (zsRef) {
333             return finished;
334         }
335     }
336
337     /**
338      * Uncompresses bytes into specified buffer. Returns actual number
339      * of bytes uncompressed. A return value of 0 indicates that
340      * needsInput() or needsDictionary() should be called in order to
341      * determine if more input data or a preset dictionary is required.
342      * In the latter case, getAdler() can be used to get the Adler-32
343      * value of the dictionary required.
344      * <p>
345      * If the {@link #setInput(ByteBuffer)} method was called to provide a buffer
346      * for input, the input buffer's position will be advanced by the number of bytes
347      * consumed by this operation, even in the event that a {@link DataFormatException}
348      * is thrown.
349      * <p>
350      * The {@linkplain #getRemaining() remaining byte count} will be reduced by
351      * the number of consumed input bytes.  If the {@link #setInput(ByteBuffer)}
352      * method was called to provide a buffer for input, the input buffer's position
353      * will be advanced the number of consumed bytes.
354      * <p>
355      * These byte totals, as well as
356      * the {@linkplain #getBytesRead() total bytes read}
357      * and the {@linkplain #getBytesWritten() total bytes written}
358      * values, will be updated even in the event that a {@link DataFormatException}
359      * is thrown to reflect the amount of data consumed and produced before the
360      * exception occurred.
361      *
362      * @param output the buffer for the uncompressed data
363      * @param off the start offset of the data
364      * @param len the maximum number of uncompressed bytes
365      * @return the actual number of uncompressed bytes
366      * @throws DataFormatException if the compressed data format is invalid
367      * @see Inflater#needsInput
368      * @see Inflater#needsDictionary
369      */

370     public int inflate(byte[] output, int off, int len)
371         throws DataFormatException
372     {
373         if (off < 0 || len < 0 || off > output.length - len) {
374             throw new ArrayIndexOutOfBoundsException();
375         }
376         synchronized (zsRef) {
377             ensureOpen();
378             ByteBuffer input = this.input;
379             long result;
380             int inputPos;
381             try {
382                 if (input == null) {
383                     inputPos = this.inputPos;
384                     try {
385                         result = inflateBytesBytes(zsRef.address(),
386                             inputArray, inputPos, inputLim - inputPos,
387                             output, off, len);
388                     } catch (DataFormatException e) {
389                         this.inputPos = inputPos + inputConsumed;
390                         throw e;
391                     }
392                 } else {
393                     inputPos = input.position();
394                     try {
395                         int inputRem = Math.max(input.limit() - inputPos, 0);
396                         if (input.isDirect()) {
397                             try {
398                                 long inputAddress = ((DirectBuffer) input).address();
399                                 result = inflateBufferBytes(zsRef.address(),
400                                     inputAddress + inputPos, inputRem,
401                                     output, off, len);
402                             } finally {
403                                 Reference.reachabilityFence(input);
404                             }
405                         } else {
406                             byte[] inputArray = ZipUtils.getBufferArray(input);
407                             int inputOffset = ZipUtils.getBufferOffset(input);
408                             result = inflateBytesBytes(zsRef.address(),
409                                 inputArray, inputOffset + inputPos, inputRem,
410                                 output, off, len);
411                         }
412                     } catch (DataFormatException e) {
413                         input.position(inputPos + inputConsumed);
414                         throw e;
415                     }
416                 }
417             } catch (DataFormatException e) {
418                 bytesRead += inputConsumed;
419                 inputConsumed = 0;
420                 int written = outputConsumed;
421                 bytesWritten += written;
422                 outputConsumed = 0;
423                 throw e;
424             }
425             int read = (int) (result & 0x7fff_ffffL);
426             int written = (int) (result >>> 31 & 0x7fff_ffffL);
427             if ((result >>> 62 & 1) != 0) {
428                 finished = true;
429             }
430             if ((result >>> 63 & 1) != 0) {
431                 needDict = true;
432             }
433             if (input != null) {
434                 input.position(inputPos + read);
435             } else {
436                 this.inputPos = inputPos + read;
437             }
438             bytesWritten += written;
439             bytesRead += read;
440             return written;
441         }
442     }
443
444     /**
445      * Uncompresses bytes into specified buffer. Returns actual number
446      * of bytes uncompressed. A return value of 0 indicates that
447      * needsInput() or needsDictionary() should be called in order to
448      * determine if more input data or a preset dictionary is required.
449      * In the latter case, getAdler() can be used to get the Adler-32
450      * value of the dictionary required.
451      * <p>
452      * The {@linkplain #getRemaining() remaining byte count} will be reduced by
453      * the number of consumed input bytes.  If the {@link #setInput(ByteBuffer)}
454      * method was called to provide a buffer for input, the input buffer's position
455      * will be advanced the number of consumed bytes.
456      * <p>
457      * These byte totals, as well as
458      * the {@linkplain #getBytesRead() total bytes read}
459      * and the {@linkplain #getBytesWritten() total bytes written}
460      * values, will be updated even in the event that a {@link DataFormatException}
461      * is thrown to reflect the amount of data consumed and produced before the
462      * exception occurred.
463      *
464      * @param output the buffer for the uncompressed data
465      * @return the actual number of uncompressed bytes
466      * @throws DataFormatException if the compressed data format is invalid
467      * @see Inflater#needsInput
468      * @see Inflater#needsDictionary
469      */

470     public int inflate(byte[] output) throws DataFormatException {
471         return inflate(output, 0, output.length);
472     }
473
474     /**
475      * Uncompresses bytes into specified buffer. Returns actual number
476      * of bytes uncompressed. A return value of 0 indicates that
477      * needsInput() or needsDictionary() should be called in order to
478      * determine if more input data or a preset dictionary is required.
479      * In the latter case, getAdler() can be used to get the Adler-32
480      * value of the dictionary required.
481      * <p>
482      * On success, the position of the given {@code output} byte buffer will be
483      * advanced by as many bytes as were produced by the operation, which is equal
484      * to the number returned by this method.  Note that the position of the
485      * {@code output} buffer will be advanced even in the event that a
486      * {@link DataFormatException} is thrown.
487      * <p>
488      * The {@linkplain #getRemaining() remaining byte count} will be reduced by
489      * the number of consumed input bytes.  If the {@link #setInput(ByteBuffer)}
490      * method was called to provide a buffer for input, the input buffer's position
491      * will be advanced the number of consumed bytes.
492      * <p>
493      * These byte totals, as well as
494      * the {@linkplain #getBytesRead() total bytes read}
495      * and the {@linkplain #getBytesWritten() total bytes written}
496      * values, will be updated even in the event that a {@link DataFormatException}
497      * is thrown to reflect the amount of data consumed and produced before the
498      * exception occurred.
499      *
500      * @param output the buffer for the uncompressed data
501      * @return the actual number of uncompressed bytes
502      * @throws DataFormatException if the compressed data format is invalid
503      * @throws ReadOnlyBufferException if the given output buffer is read-only
504      * @see Inflater#needsInput
505      * @see Inflater#needsDictionary
506      * @since 11
507      */

508     public int inflate(ByteBuffer output) throws DataFormatException {
509         if (output.isReadOnly()) {
510             throw new ReadOnlyBufferException();
511         }
512         synchronized (zsRef) {
513             ensureOpen();
514             ByteBuffer input = this.input;
515             long result;
516             int inputPos;
517             int outputPos = output.position();
518             int outputRem = Math.max(output.limit() - outputPos, 0);
519             try {
520                 if (input == null) {
521                     inputPos = this.inputPos;
522                     try {
523                         if (output.isDirect()) {
524                             long outputAddress = ((DirectBuffer) output).address();
525                             try {
526                                 result = inflateBytesBuffer(zsRef.address(),
527                                     inputArray, inputPos, inputLim - inputPos,
528                                     outputAddress + outputPos, outputRem);
529                             } finally {
530                                 Reference.reachabilityFence(output);
531                             }
532                         } else {
533                             byte[] outputArray = ZipUtils.getBufferArray(output);
534                             int outputOffset = ZipUtils.getBufferOffset(output);
535                             result = inflateBytesBytes(zsRef.address(),
536                                 inputArray, inputPos, inputLim - inputPos,
537                                 outputArray, outputOffset + outputPos, outputRem);
538                         }
539                     } catch (DataFormatException e) {
540                         this.inputPos = inputPos + inputConsumed;
541                         throw e;
542                     }
543                 } else {
544                     inputPos = input.position();
545                     int inputRem = Math.max(input.limit() - inputPos, 0);
546                     try {
547                         if (input.isDirect()) {
548                             long inputAddress = ((DirectBuffer) input).address();
549                             try {
550                                 if (output.isDirect()) {
551                                     long outputAddress = ((DirectBuffer) output).address();
552                                     try {
553                                         result = inflateBufferBuffer(zsRef.address(),
554                                             inputAddress + inputPos, inputRem,
555                                             outputAddress + outputPos, outputRem);
556                                     } finally {
557                                         Reference.reachabilityFence(output);
558                                     }
559                                 } else {
560                                     byte[] outputArray = ZipUtils.getBufferArray(output);
561                                     int outputOffset = ZipUtils.getBufferOffset(output);
562                                     result = inflateBufferBytes(zsRef.address(),
563                                         inputAddress + inputPos, inputRem,
564                                         outputArray, outputOffset + outputPos, outputRem);
565                                 }
566                             } finally {
567                                 Reference.reachabilityFence(input);
568                             }
569                         } else {
570                             byte[] inputArray = ZipUtils.getBufferArray(input);
571                             int inputOffset = ZipUtils.getBufferOffset(input);
572                             if (output.isDirect()) {
573                                 long outputAddress = ((DirectBuffer) output).address();
574                                 try {
575                                     result = inflateBytesBuffer(zsRef.address(),
576                                         inputArray, inputOffset + inputPos, inputRem,
577                                         outputAddress + outputPos, outputRem);
578                                 } finally {
579                                     Reference.reachabilityFence(output);
580                                 }
581                             } else {
582                                 byte[] outputArray = ZipUtils.getBufferArray(output);
583                                 int outputOffset = ZipUtils.getBufferOffset(output);
584                                 result = inflateBytesBytes(zsRef.address(),
585                                     inputArray, inputOffset + inputPos, inputRem,
586                                     outputArray, outputOffset + outputPos, outputRem);
587                             }
588                         }
589                     } catch (DataFormatException e) {
590                         input.position(inputPos + inputConsumed);
591                         throw e;
592                     }
593                 }
594             } catch (DataFormatException e) {
595                 bytesRead += inputConsumed;
596                 inputConsumed = 0;
597                 int written = outputConsumed;
598                 output.position(outputPos + written);
599                 bytesWritten += written;
600                 outputConsumed = 0;
601                 throw e;
602             }
603             int read = (int) (result & 0x7fff_ffffL);
604             int written = (int) (result >>> 31 & 0x7fff_ffffL);
605             if ((result >>> 62 & 1) != 0) {
606                 finished = true;
607             }
608             if ((result >>> 63 & 1) != 0) {
609                 needDict = true;
610             }
611             if (input != null) {
612                 input.position(inputPos + read);
613             } else {
614                 this.inputPos = inputPos + read;
615             }
616             // Note: this method call also serves to keep the byteBuffer ref alive
617             output.position(outputPos + written);
618             bytesWritten += written;
619             bytesRead += read;
620             return written;
621         }
622     }
623
624     /**
625      * Returns the ADLER-32 value of the uncompressed data.
626      * @return the ADLER-32 value of the uncompressed data
627      */

628     public int getAdler() {
629         synchronized (zsRef) {
630             ensureOpen();
631             return getAdler(zsRef.address());
632         }
633     }
634
635     /**
636      * Returns the total number of compressed bytes input so far.
637      *
638      * <p>Since the number of bytes may be greater than
639      * Integer.MAX_VALUE, the {@link #getBytesRead()} method is now
640      * the preferred means of obtaining this information.</p>
641      *
642      * @return the total number of compressed bytes input so far
643      */

644     public int getTotalIn() {
645         return (int) getBytesRead();
646     }
647
648     /**
649      * Returns the total number of compressed bytes input so far.
650      *
651      * @return the total (non-negative) number of compressed bytes input so far
652      * @since 1.5
653      */

654     public long getBytesRead() {
655         synchronized (zsRef) {
656             ensureOpen();
657             return bytesRead;
658         }
659     }
660
661     /**
662      * Returns the total number of uncompressed bytes output so far.
663      *
664      * <p>Since the number of bytes may be greater than
665      * Integer.MAX_VALUE, the {@link #getBytesWritten()} method is now
666      * the preferred means of obtaining this information.</p>
667      *
668      * @return the total number of uncompressed bytes output so far
669      */

670     public int getTotalOut() {
671         return (int) getBytesWritten();
672     }
673
674     /**
675      * Returns the total number of uncompressed bytes output so far.
676      *
677      * @return the total (non-negative) number of uncompressed bytes output so far
678      * @since 1.5
679      */

680     public long getBytesWritten() {
681         synchronized (zsRef) {
682             ensureOpen();
683             return bytesWritten;
684         }
685     }
686
687     /**
688      * Resets inflater so that a new set of input data can be processed.
689      */

690     public void reset() {
691         synchronized (zsRef) {
692             ensureOpen();
693             reset(zsRef.address());
694             input = ZipUtils.defaultBuf;
695             inputArray = null;
696             finished = false;
697             needDict = false;
698             bytesRead = bytesWritten = 0;
699         }
700     }
701
702     /**
703      * Closes the decompressor and discards any unprocessed input.
704      *
705      * This method should be called when the decompressor is no longer
706      * being used. Once this method is called, the behavior of the
707      * Inflater object is undefined.
708      */

709     public void end() {
710         synchronized (zsRef) {
711             zsRef.clean();
712             input = ZipUtils.defaultBuf;
713             inputArray = null;
714         }
715     }
716
717     /**
718      * Closes the decompressor when garbage is collected.
719      *
720      * @implSpec
721      * If this {@code Inflater} has been subclassed and the {@code end} method
722      * has been overridden, the {@code end} method will be called when the
723      * inflater is unreachable.
724      *
725      * @deprecated The {@code finalize} method has been deprecated and will be
726      *     removed. It is implemented as a no-op. Subclasses that override
727      *     {@code finalize} in order to perform cleanup should be modified to use
728      *     alternative cleanup mechanisms and remove the overriding {@code finalize}
729      *     method. The recommended cleanup for compressor is to explicitly call
730      *     {@code end} method when it is no longer in use. If the {@code end} is
731      *     not invoked explicitly the resource of the compressor will be released
732      *     when the instance becomes unreachable,
733      */

734     @Deprecated(since="9", forRemoval=true)
735     protected void finalize() {}
736
737     private void ensureOpen () {
738         assert Thread.holdsLock(zsRef);
739         if (zsRef.address() == 0)
740             throw new NullPointerException("Inflater has been closed");
741     }
742
743     private static native void initIDs();
744     private static native long init(boolean nowrap);
745     private static native void setDictionary(long addr, byte[] b, int off,
746                                              int len);
747     private static native void setDictionaryBuffer(long addr, long bufAddress, int len);
748     private native long inflateBytesBytes(long addr,
749         byte[] inputArray, int inputOff, int inputLen,
750         byte[] outputArray, int outputOff, int outputLen) throws DataFormatException;
751     private native long inflateBytesBuffer(long addr,
752         byte[] inputArray, int inputOff, int inputLen,
753         long outputAddress, int outputLen) throws DataFormatException;
754     private native long inflateBufferBytes(long addr,
755         long inputAddress, int inputLen,
756         byte[] outputArray, int outputOff, int outputLen) throws DataFormatException;
757     private native long inflateBufferBuffer(long addr,
758         long inputAddress, int inputLen,
759         long outputAddress, int outputLen) throws DataFormatException;
760     private static native int getAdler(long addr);
761     private static native void reset(long addr);
762     private static native void end(long addr);
763
764     /**
765      * A reference to the native zlib's z_stream structure. It also
766      * serves as the "cleaner" to clean up the native resource when
767      * the Inflater is ended, closed or cleaned.
768      */

769     static class InflaterZStreamRef implements Runnable {
770
771         private long address;
772         private final Cleanable cleanable;
773
774         private InflaterZStreamRef(Inflater owner, long addr) {
775             this.cleanable = (owner != null) ? CleanerFactory.cleaner().register(owner, this) : null;
776             this.address = addr;
777         }
778
779         long address() {
780             return address;
781         }
782
783         void clean() {
784             cleanable.clean();
785         }
786
787         public synchronized void run() {
788             long addr = address;
789             address = 0;
790             if (addr != 0) {
791                 end(addr);
792             }
793         }
794
795         /*
796          * If {@code Inflater} has been subclassed and the {@code end} method is
797          * overridden, uses {@code finalizer} mechanism for resource cleanup. So
798          * {@code end} method can be called when the {@code Inflater} is unreachable.
799          * This mechanism will be removed when the {@code finalize} method is
800          * removed from {@code Inflater}.
801          */

802         static InflaterZStreamRef get(Inflater owner, long addr) {
803             Class<?> clz = owner.getClass();
804             while (clz != Inflater.class) {
805                 try {
806                     clz.getDeclaredMethod("end");
807                     return new FinalizableZStreamRef(owner, addr);
808                 } catch (NoSuchMethodException nsme) {}
809                 clz = clz.getSuperclass();
810             }
811             return new InflaterZStreamRef(owner, addr);
812         }
813
814         private static class FinalizableZStreamRef extends InflaterZStreamRef {
815             final Inflater owner;
816
817             FinalizableZStreamRef(Inflater owner, long addr) {
818                 super(null, addr);
819                 this.owner = owner;
820             }
821
822             @Override
823             void clean() {
824                 run();
825             }
826
827             @Override
828             @SuppressWarnings("deprecation")
829             protected void finalize() {
830                 owner.end();
831             }
832         }
833     }
834 }
835