8203328: Rename EFS in java.util.zip internals to something meaningful
Reviewed-by: sherman

   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 }
--- EOF ---