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  * @see         Deflater
  91  * @author      David Connelly
  92  * @since 1.1
  93  *
  94  */
  95 
  96 public class Inflater {
  97 
  98     private final InflaterZStreamRef zsRef;
  99     private ByteBuffer input = ZipUtils.defaultBuf;
 100     private byte[] inputArray;
 101     private int inputPos, inputLim;
 102     private boolean finished;
 103     private boolean needDict;
 104     private long bytesRead;
 105     private long bytesWritten;
 106 
 107     /*
 108      * These fields are used as an "out" parameter from JNI when a
 109      * DataFormatException is thrown during the inflate operation.
 110      */
 111     private int inputConsumed;
 112     private int outputConsumed;
 113 
 114     static {
 115         ZipUtils.loadLibrary();
 116         initIDs();
 117     }
 118 
 119     /**
 120      * Creates a new decompressor. If the parameter 'nowrap' is true then
 121      * the ZLIB header and checksum fields will not be used. This provides
 122      * compatibility with the compression format used by both GZIP and PKZIP.
 123      * <p>
 124      * Note: When using the 'nowrap' option it is also necessary to provide
 125      * an extra "dummy" byte as input. This is required by the ZLIB native
 126      * library in order to support certain optimizations.
 127      *
 128      * @param nowrap if true then support GZIP compatible compression
 129      */
 130     public Inflater(boolean nowrap) {
 131         this.zsRef = new InflaterZStreamRef(this, init(nowrap));
 132     }
 133 
 134     /**
 135      * Creates a new decompressor.
 136      */
 137     public Inflater() {
 138         this(false);
 139     }
 140 
 141     /**
 142      * Sets input data for decompression.
 143      * <p>
 144      * One of the {@code setInput()} methods should be called whenever
 145      * {@code needsInput()} returns true indicating that more input data
 146      * is required.
 147      *
 148      * @param input the input data bytes
 149      * @param off the start offset of the input data
 150      * @param len the length of the input data
 151      * @see Inflater#needsInput
 152      */
 153     public void setInput(byte[] input, int off, int len) {
 154         if (off < 0 || len < 0 || off > input.length - len) {
 155             throw new ArrayIndexOutOfBoundsException();
 156         }
 157         synchronized (zsRef) {
 158             this.input = null;
 159             this.inputArray = input;
 160             this.inputPos = off;
 161             this.inputLim = off + len;
 162         }
 163     }
 164 
 165     /**
 166      * Sets input data for decompression.
 167      * <p>
 168      * One of the {@code setInput()} methods should be called whenever
 169      * {@code needsInput()} returns true indicating that more input data
 170      * is required.
 171      *
 172      * @param input the input data bytes
 173      * @see Inflater#needsInput
 174      */
 175     public void setInput(byte[] input) {
 176         setInput(input, 0, input.length);
 177     }
 178 
 179     /**
 180      * Sets input data for decompression.
 181      * <p>
 182      * One of the {@code setInput()} methods should be called whenever
 183      * {@code needsInput()} returns true indicating that more input data
 184      * is required.
 185      * <p>
 186      * The given buffer's position will be advanced as inflate
 187      * operations are performed, up to the buffer's limit.
 188      * The input buffer may be modified (refilled) between inflate
 189      * operations; doing so is equivalent to creating a new buffer
 190      * and setting it with this method.
 191      * <p>
 192      * Modifying the input buffer's contents, position, or limit
 193      * concurrently with an inflate operation will result in
 194      * undefined behavior, which may include incorrect operation
 195      * results or operation failure.
 196      *
 197      * @param input the input data bytes
 198      * @see Inflater#needsInput
 199      * @since 11
 200      */
 201     public void setInput(ByteBuffer input) {
 202         Objects.requireNonNull(input);
 203         synchronized (zsRef) {
 204             this.input = input;
 205             this.inputArray = null;
 206         }
 207     }
 208 
 209     /**
 210      * Sets the preset dictionary to the given array of bytes. Should be
 211      * called when inflate() returns 0 and needsDictionary() returns true
 212      * indicating that a preset dictionary is required. The method getAdler()
 213      * can be used to get the Adler-32 value of the dictionary needed.
 214      * @param dictionary the dictionary data bytes
 215      * @param off the start offset of the data
 216      * @param len the length of the data
 217      * @see Inflater#needsDictionary
 218      * @see Inflater#getAdler
 219      */
 220     public void setDictionary(byte[] dictionary, int off, int len) {
 221         if (off < 0 || len < 0 || off > dictionary.length - len) {
 222             throw new ArrayIndexOutOfBoundsException();
 223         }
 224         synchronized (zsRef) {
 225             ensureOpen();
 226             setDictionary(zsRef.address(), dictionary, off, len);
 227             needDict = false;
 228         }
 229     }
 230 
 231     /**
 232      * Sets the preset dictionary to the given array of bytes. Should be
 233      * called when inflate() returns 0 and needsDictionary() returns true
 234      * indicating that a preset dictionary is required. The method getAdler()
 235      * can be used to get the Adler-32 value of the dictionary needed.
 236      * @param dictionary the dictionary data bytes
 237      * @see Inflater#needsDictionary
 238      * @see Inflater#getAdler
 239      */
 240     public void setDictionary(byte[] dictionary) {
 241         setDictionary(dictionary, 0, dictionary.length);
 242     }
 243 
 244     /**
 245      * Sets the preset dictionary to the bytes in the given buffer. Should be
 246      * called when inflate() returns 0 and needsDictionary() returns true
 247      * indicating that a preset dictionary is required. The method getAdler()
 248      * can be used to get the Adler-32 value of the dictionary needed.
 249      * <p>
 250      * The bytes in given byte buffer will be fully consumed by this method.  On
 251      * return, its position will equal its limit.
 252      *
 253      * @param dictionary the dictionary data bytes
 254      * @see Inflater#needsDictionary
 255      * @see Inflater#getAdler
 256      * @since 11
 257      */
 258     public void setDictionary(ByteBuffer dictionary) {
 259         synchronized (zsRef) {
 260             int position = dictionary.position();
 261             int remaining = Math.max(dictionary.limit() - position, 0);
 262             ensureOpen();
 263             if (dictionary.isDirect()) {
 264                 long address = ((DirectBuffer) dictionary).address();
 265                 try {
 266                     setDictionaryBuffer(zsRef.address(), address + position, remaining);
 267                 } finally {
 268                     Reference.reachabilityFence(dictionary);
 269                 }
 270             } else {
 271                 byte[] array = ZipUtils.getBufferArray(dictionary);
 272                 int offset = ZipUtils.getBufferOffset(dictionary);
 273                 setDictionary(zsRef.address(), array, offset + position, remaining);
 274             }
 275             dictionary.position(position + remaining);
 276             needDict = false;
 277         }
 278     }
 279 
 280     /**
 281      * Returns the total number of bytes remaining in the input buffer.
 282      * This can be used to find out what bytes still remain in the input
 283      * buffer after decompression has finished.
 284      * @return the total number of bytes remaining in the input buffer
 285      */
 286     public int getRemaining() {
 287         synchronized (zsRef) {
 288             ByteBuffer input = this.input;
 289             return input == null ? inputLim - inputPos : input.remaining();
 290         }
 291     }
 292 
 293     /**
 294      * Returns true if no data remains in the input buffer. This can
 295      * be used to determine if one of the {@code setInput()} methods should be
 296      * called in order to provide more input.
 297      *
 298      * @return true if no data remains in the input buffer
 299      */
 300     public boolean needsInput() {
 301         synchronized (zsRef) {
 302             ByteBuffer input = this.input;
 303             return input == null ? inputLim == inputPos : ! input.hasRemaining();
 304         }
 305     }
 306 
 307     /**
 308      * Returns true if a preset dictionary is needed for decompression.
 309      * @return true if a preset dictionary is needed for decompression
 310      * @see Inflater#setDictionary
 311      */
 312     public boolean needsDictionary() {
 313         synchronized (zsRef) {
 314             return needDict;
 315         }
 316     }
 317 
 318     /**
 319      * Returns true if the end of the compressed data stream has been
 320      * reached.
 321      * @return true if the end of the compressed data stream has been
 322      * reached
 323      */
 324     public boolean finished() {
 325         synchronized (zsRef) {
 326             return finished;
 327         }
 328     }
 329 
 330     /**
 331      * Uncompresses bytes into specified buffer. Returns actual number
 332      * of bytes uncompressed. A return value of 0 indicates that
 333      * needsInput() or needsDictionary() should be called in order to
 334      * determine if more input data or a preset dictionary is required.
 335      * In the latter case, getAdler() can be used to get the Adler-32
 336      * value of the dictionary required.
 337      * <p>
 338      * If the {@link #setInput(ByteBuffer)} method was called to provide a buffer
 339      * for input, the input buffer's position will be advanced by the number of bytes
 340      * consumed by this operation, even in the event that a {@link DataFormatException}
 341      * is thrown.
 342      * <p>
 343      * The {@linkplain #getRemaining() remaining byte count} will be reduced by
 344      * the number of consumed input bytes.  If the {@link #setInput(ByteBuffer)}
 345      * method was called to provide a buffer for input, the input buffer's position
 346      * will be advanced the number of consumed bytes.
 347      * <p>
 348      * These byte totals, as well as
 349      * the {@linkplain #getBytesRead() total bytes read}
 350      * and the {@linkplain #getBytesWritten() total bytes written}
 351      * values, will be updated even in the event that a {@link DataFormatException}
 352      * is thrown to reflect the amount of data consumed and produced before the
 353      * exception occurred.
 354      *
 355      * @param output the buffer for the uncompressed data
 356      * @param off the start offset of the data
 357      * @param len the maximum number of uncompressed bytes
 358      * @return the actual number of uncompressed bytes
 359      * @throws DataFormatException if the compressed data format is invalid
 360      * @see Inflater#needsInput
 361      * @see Inflater#needsDictionary
 362      */
 363     public int inflate(byte[] output, int off, int len)
 364         throws DataFormatException
 365     {
 366         if (off < 0 || len < 0 || off > output.length - len) {
 367             throw new ArrayIndexOutOfBoundsException();
 368         }
 369         synchronized (zsRef) {
 370             ensureOpen();
 371             ByteBuffer input = this.input;
 372             long result;
 373             int inputPos;
 374             try {
 375                 if (input == null) {
 376                     inputPos = this.inputPos;
 377                     try {
 378                         result = inflateBytesBytes(zsRef.address(),
 379                             inputArray, inputPos, inputLim - inputPos,
 380                             output, off, len);
 381                     } catch (DataFormatException e) {
 382                         this.inputPos = inputPos + inputConsumed;
 383                         throw e;
 384                     }
 385                 } else {
 386                     inputPos = input.position();
 387                     try {
 388                         int inputRem = Math.max(input.limit() - inputPos, 0);
 389                         if (input.isDirect()) {
 390                             try {
 391                                 long inputAddress = ((DirectBuffer) input).address();
 392                                 result = inflateBufferBytes(zsRef.address(),
 393                                     inputAddress + inputPos, inputRem,
 394                                     output, off, len);
 395                             } finally {
 396                                 Reference.reachabilityFence(input);
 397                             }
 398                         } else {
 399                             byte[] inputArray = ZipUtils.getBufferArray(input);
 400                             int inputOffset = ZipUtils.getBufferOffset(input);
 401                             result = inflateBytesBytes(zsRef.address(),
 402                                 inputArray, inputOffset + inputPos, inputRem,
 403                                 output, off, len);
 404                         }
 405                     } catch (DataFormatException e) {
 406                         input.position(inputPos + inputConsumed);
 407                         throw e;
 408                     }
 409                 }
 410             } catch (DataFormatException e) {
 411                 bytesRead += inputConsumed;
 412                 inputConsumed = 0;
 413                 int written = outputConsumed;
 414                 bytesWritten += written;
 415                 outputConsumed = 0;
 416                 throw e;
 417             }
 418             int read = (int) (result & 0x7fff_ffffL);
 419             int written = (int) (result >>> 31 & 0x7fff_ffffL);
 420             if ((result >>> 62 & 1) != 0) {
 421                 finished = true;
 422             }
 423             if ((result >>> 63 & 1) != 0) {
 424                 needDict = true;
 425             }
 426             if (input != null) {
 427                 input.position(inputPos + read);
 428             } else {
 429                 this.inputPos = inputPos + read;
 430             }
 431             bytesWritten += written;
 432             bytesRead += read;
 433             return written;
 434         }
 435     }
 436 
 437     /**
 438      * Uncompresses bytes into specified buffer. Returns actual number
 439      * of bytes uncompressed. A return value of 0 indicates that
 440      * needsInput() or needsDictionary() should be called in order to
 441      * determine if more input data or a preset dictionary is required.
 442      * In the latter case, getAdler() can be used to get the Adler-32
 443      * value of the dictionary required.
 444      * <p>
 445      * The {@linkplain #getRemaining() remaining byte count} will be reduced by
 446      * the number of consumed input bytes.  If the {@link #setInput(ByteBuffer)}
 447      * method was called to provide a buffer for input, the input buffer's position
 448      * will be advanced the number of consumed bytes.
 449      * <p>
 450      * These byte totals, as well as
 451      * the {@linkplain #getBytesRead() total bytes read}
 452      * and the {@linkplain #getBytesWritten() total bytes written}
 453      * values, will be updated even in the event that a {@link DataFormatException}
 454      * is thrown to reflect the amount of data consumed and produced before the
 455      * exception occurred.
 456      *
 457      * @param output the buffer for the uncompressed data
 458      * @return the actual number of uncompressed bytes
 459      * @throws DataFormatException if the compressed data format is invalid
 460      * @see Inflater#needsInput
 461      * @see Inflater#needsDictionary
 462      */
 463     public int inflate(byte[] output) throws DataFormatException {
 464         return inflate(output, 0, output.length);
 465     }
 466 
 467     /**
 468      * Uncompresses bytes into specified buffer. Returns actual number
 469      * of bytes uncompressed. A return value of 0 indicates that
 470      * needsInput() or needsDictionary() should be called in order to
 471      * determine if more input data or a preset dictionary is required.
 472      * In the latter case, getAdler() can be used to get the Adler-32
 473      * value of the dictionary required.
 474      * <p>
 475      * On success, the position of the given {@code output} byte buffer will be
 476      * advanced by as many bytes as were produced by the operation, which is equal
 477      * to the number returned by this method.  Note that the position of the
 478      * {@code output} buffer will be advanced even in the event that a
 479      * {@link DataFormatException} is thrown.
 480      * <p>
 481      * The {@linkplain #getRemaining() remaining byte count} will be reduced by
 482      * the number of consumed input bytes.  If the {@link #setInput(ByteBuffer)}
 483      * method was called to provide a buffer for input, the input buffer's position
 484      * will be advanced the number of consumed bytes.
 485      * <p>
 486      * These byte totals, as well as
 487      * the {@linkplain #getBytesRead() total bytes read}
 488      * and the {@linkplain #getBytesWritten() total bytes written}
 489      * values, will be updated even in the event that a {@link DataFormatException}
 490      * is thrown to reflect the amount of data consumed and produced before the
 491      * exception occurred.
 492      *
 493      * @param output the buffer for the uncompressed data
 494      * @return the actual number of uncompressed bytes
 495      * @throws DataFormatException if the compressed data format is invalid
 496      * @throws ReadOnlyBufferException if the given output buffer is read-only
 497      * @see Inflater#needsInput
 498      * @see Inflater#needsDictionary
 499      * @since 11
 500      */
 501     public int inflate(ByteBuffer output) throws DataFormatException {
 502         if (output.isReadOnly()) {
 503             throw new ReadOnlyBufferException();
 504         }
 505         synchronized (zsRef) {
 506             ensureOpen();
 507             ByteBuffer input = this.input;
 508             long result;
 509             int inputPos;
 510             int outputPos = output.position();
 511             int outputRem = Math.max(output.limit() - outputPos, 0);
 512             try {
 513                 if (input == null) {
 514                     inputPos = this.inputPos;
 515                     try {
 516                         if (output.isDirect()) {
 517                             long outputAddress = ((DirectBuffer) output).address();
 518                             try {
 519                                 result = inflateBytesBuffer(zsRef.address(),
 520                                     inputArray, inputPos, inputLim - inputPos,
 521                                     outputAddress + outputPos, outputRem);
 522                             } finally {
 523                                 Reference.reachabilityFence(output);
 524                             }
 525                         } else {
 526                             byte[] outputArray = ZipUtils.getBufferArray(output);
 527                             int outputOffset = ZipUtils.getBufferOffset(output);
 528                             result = inflateBytesBytes(zsRef.address(),
 529                                 inputArray, inputPos, inputLim - inputPos,
 530                                 outputArray, outputOffset + outputPos, outputRem);
 531                         }
 532                     } catch (DataFormatException e) {
 533                         this.inputPos = inputPos + inputConsumed;
 534                         throw e;
 535                     }
 536                 } else {
 537                     inputPos = input.position();
 538                     int inputRem = Math.max(input.limit() - inputPos, 0);
 539                     try {
 540                         if (input.isDirect()) {
 541                             long inputAddress = ((DirectBuffer) input).address();
 542                             try {
 543                                 if (output.isDirect()) {
 544                                     long outputAddress = ((DirectBuffer) output).address();
 545                                     try {
 546                                         result = inflateBufferBuffer(zsRef.address(),
 547                                             inputAddress + inputPos, inputRem,
 548                                             outputAddress + outputPos, outputRem);
 549                                     } finally {
 550                                         Reference.reachabilityFence(output);
 551                                     }
 552                                 } else {
 553                                     byte[] outputArray = ZipUtils.getBufferArray(output);
 554                                     int outputOffset = ZipUtils.getBufferOffset(output);
 555                                     result = inflateBufferBytes(zsRef.address(),
 556                                         inputAddress + inputPos, inputRem,
 557                                         outputArray, outputOffset + outputPos, outputRem);
 558                                 }
 559                             } finally {
 560                                 Reference.reachabilityFence(input);
 561                             }
 562                         } else {
 563                             byte[] inputArray = ZipUtils.getBufferArray(input);
 564                             int inputOffset = ZipUtils.getBufferOffset(input);
 565                             if (output.isDirect()) {
 566                                 long outputAddress = ((DirectBuffer) output).address();
 567                                 try {
 568                                     result = inflateBytesBuffer(zsRef.address(),
 569                                         inputArray, inputOffset + inputPos, inputRem,
 570                                         outputAddress + outputPos, outputRem);
 571                                 } finally {
 572                                     Reference.reachabilityFence(output);
 573                                 }
 574                             } else {
 575                                 byte[] outputArray = ZipUtils.getBufferArray(output);
 576                                 int outputOffset = ZipUtils.getBufferOffset(output);
 577                                 result = inflateBytesBytes(zsRef.address(),
 578                                     inputArray, inputOffset + inputPos, inputRem,
 579                                     outputArray, outputOffset + outputPos, outputRem);
 580                             }
 581                         }
 582                     } catch (DataFormatException e) {
 583                         input.position(inputPos + inputConsumed);
 584                         throw e;
 585                     }
 586                 }
 587             } catch (DataFormatException e) {
 588                 bytesRead += inputConsumed;
 589                 inputConsumed = 0;
 590                 int written = outputConsumed;
 591                 output.position(outputPos + written);
 592                 bytesWritten += written;
 593                 outputConsumed = 0;
 594                 throw e;
 595             }
 596             int read = (int) (result & 0x7fff_ffffL);
 597             int written = (int) (result >>> 31 & 0x7fff_ffffL);
 598             if ((result >>> 62 & 1) != 0) {
 599                 finished = true;
 600             }
 601             if ((result >>> 63 & 1) != 0) {
 602                 needDict = true;
 603             }
 604             if (input != null) {
 605                 input.position(inputPos + read);
 606             } else {
 607                 this.inputPos = inputPos + read;
 608             }
 609             // Note: this method call also serves to keep the byteBuffer ref alive
 610             output.position(outputPos + written);
 611             bytesWritten += written;
 612             bytesRead += read;
 613             return written;
 614         }
 615     }
 616 
 617     /**
 618      * Returns the ADLER-32 value of the uncompressed data.
 619      * @return the ADLER-32 value of the uncompressed data
 620      */
 621     public int getAdler() {
 622         synchronized (zsRef) {
 623             ensureOpen();
 624             return getAdler(zsRef.address());
 625         }
 626     }
 627 
 628     /**
 629      * Returns the total number of compressed bytes input so far.
 630      *
 631      * <p>Since the number of bytes may be greater than
 632      * Integer.MAX_VALUE, the {@link #getBytesRead()} method is now
 633      * the preferred means of obtaining this information.</p>
 634      *
 635      * @return the total number of compressed bytes input so far
 636      */
 637     public int getTotalIn() {
 638         return (int) getBytesRead();
 639     }
 640 
 641     /**
 642      * Returns the total number of compressed bytes input so far.
 643      *
 644      * @return the total (non-negative) number of compressed bytes input so far
 645      * @since 1.5
 646      */
 647     public long getBytesRead() {
 648         synchronized (zsRef) {
 649             ensureOpen();
 650             return bytesRead;
 651         }
 652     }
 653 
 654     /**
 655      * Returns the total number of uncompressed bytes output so far.
 656      *
 657      * <p>Since the number of bytes may be greater than
 658      * Integer.MAX_VALUE, the {@link #getBytesWritten()} method is now
 659      * the preferred means of obtaining this information.</p>
 660      *
 661      * @return the total number of uncompressed bytes output so far
 662      */
 663     public int getTotalOut() {
 664         return (int) getBytesWritten();
 665     }
 666 
 667     /**
 668      * Returns the total number of uncompressed bytes output so far.
 669      *
 670      * @return the total (non-negative) number of uncompressed bytes output so far
 671      * @since 1.5
 672      */
 673     public long getBytesWritten() {
 674         synchronized (zsRef) {
 675             ensureOpen();
 676             return bytesWritten;
 677         }
 678     }
 679 
 680     /**
 681      * Resets inflater so that a new set of input data can be processed.
 682      */
 683     public void reset() {
 684         synchronized (zsRef) {
 685             ensureOpen();
 686             reset(zsRef.address());
 687             input = ZipUtils.defaultBuf;
 688             inputArray = null;
 689             finished = false;
 690             needDict = false;
 691             bytesRead = bytesWritten = 0;
 692         }
 693     }
 694 
 695     /**
 696      * Closes the decompressor and discards any unprocessed input.
 697      *
 698      * This method should be called when the decompressor is no longer
 699      * being used. Once this method is called, the behavior of the
 700      * Inflater object is undefined.
 701      */
 702     public void end() {
 703         synchronized (zsRef) {
 704             zsRef.clean();
 705             input = ZipUtils.defaultBuf;
 706             inputArray = null;
 707         }
 708     }
 709 
 710 
 711     private void ensureOpen () {
 712         assert Thread.holdsLock(zsRef);
 713         if (zsRef.address() == 0)
 714             throw new NullPointerException("Inflater has been closed");
 715     }
 716 
 717     private static native void initIDs();
 718     private static native long init(boolean nowrap);
 719     private static native void setDictionary(long addr, byte[] b, int off,
 720                                              int len);
 721     private static native void setDictionaryBuffer(long addr, long bufAddress, int len);
 722     private native long inflateBytesBytes(long addr,
 723         byte[] inputArray, int inputOff, int inputLen,
 724         byte[] outputArray, int outputOff, int outputLen) throws DataFormatException;
 725     private native long inflateBytesBuffer(long addr,
 726         byte[] inputArray, int inputOff, int inputLen,
 727         long outputAddress, int outputLen) throws DataFormatException;
 728     private native long inflateBufferBytes(long addr,
 729         long inputAddress, int inputLen,
 730         byte[] outputArray, int outputOff, int outputLen) throws DataFormatException;
 731     private native long inflateBufferBuffer(long addr,
 732         long inputAddress, int inputLen,
 733         long outputAddress, int outputLen) throws DataFormatException;
 734     private static native int getAdler(long addr);
 735     private static native void reset(long addr);
 736     private static native void end(long addr);
 737 
 738     /**
 739      * A reference to the native zlib's z_stream structure. It also
 740      * serves as the "cleaner" to clean up the native resource when
 741      * the Inflater is ended, closed or cleaned.
 742      */
 743     static class InflaterZStreamRef implements Runnable {
 744 
 745         private long address;
 746         private final Cleanable cleanable;
 747 
 748         private InflaterZStreamRef(Inflater owner, long addr) {
 749             this.cleanable = (owner != null) ? CleanerFactory.cleaner().register(owner, this) : null;
 750             this.address = addr;
 751         }
 752 
 753         long address() {
 754             return address;
 755         }
 756 
 757         void clean() {
 758             cleanable.clean();
 759         }
 760 
 761         public synchronized void run() {
 762             long addr = address;
 763             address = 0;
 764             if (addr != 0) {
 765                 end(addr);
 766             }
 767         }
 768 
 769     }
 770 }