emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/Inflater.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Thu, 07 Feb 2013 12:58:12 +0100
branchemul
changeset 694 0d277415ed02
parent 687 emul/mini/src/main/java/java/util/zip/Inflater.java@a9e506a27b55
permissions -rw-r--r--
Rebasing the Inflater support on jzlib which, unlike GNU ClassPath, has correct implementation of Huffman code. Making the implementation more easily testable by turning Inflater and ZipInputStream into pure delegates. Current implementation is going to need proper long support.
jaroslav@694
     1
/*
jaroslav@694
     2
 * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
jaroslav@694
     3
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
jaroslav@694
     4
 *
jaroslav@694
     5
 * This code is free software; you can redistribute it and/or modify it
jaroslav@694
     6
 * under the terms of the GNU General Public License version 2 only, as
jaroslav@694
     7
 * published by the Free Software Foundation.  Oracle designates this
jaroslav@694
     8
 * particular file as subject to the "Classpath" exception as provided
jaroslav@694
     9
 * by Oracle in the LICENSE file that accompanied this code.
jaroslav@694
    10
 *
jaroslav@694
    11
 * This code is distributed in the hope that it will be useful, but WITHOUT
jaroslav@694
    12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
jaroslav@694
    13
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
jaroslav@694
    14
 * version 2 for more details (a copy is included in the LICENSE file that
jaroslav@694
    15
 * accompanied this code).
jaroslav@694
    16
 *
jaroslav@694
    17
 * You should have received a copy of the GNU General Public License version
jaroslav@694
    18
 * 2 along with this work; if not, write to the Free Software Foundation,
jaroslav@694
    19
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
jaroslav@694
    20
 *
jaroslav@694
    21
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
jaroslav@694
    22
 * or visit www.oracle.com if you need additional information or have any
jaroslav@694
    23
 * questions.
jaroslav@694
    24
 */
jaroslav@640
    25
jaroslav@694
    26
package org.apidesign.bck2brwsr.emul.zip;
jaroslav@640
    27
jaroslav@694
    28
import java.util.zip.*;
jaroslav@694
    29
import java.io.IOException;
jaroslav@611
    30
jaroslav@609
    31
/**
jaroslav@609
    32
 * This class provides support for general purpose decompression using the
jaroslav@609
    33
 * popular ZLIB compression library. The ZLIB compression library was
jaroslav@609
    34
 * initially developed as part of the PNG graphics standard and is not
jaroslav@609
    35
 * protected by patents. It is fully described in the specifications at
jaroslav@609
    36
 * the <a href="package-summary.html#package_description">java.util.zip
jaroslav@609
    37
 * package description</a>.
jaroslav@609
    38
 *
jaroslav@609
    39
 * <p>The following code fragment demonstrates a trivial compression
jaroslav@609
    40
 * and decompression of a string using <tt>Deflater</tt> and
jaroslav@609
    41
 * <tt>Inflater</tt>.
jaroslav@609
    42
 *
jaroslav@609
    43
 * <blockquote><pre>
jaroslav@609
    44
 * try {
jaroslav@609
    45
 *     // Encode a String into bytes
jaroslav@609
    46
 *     String inputString = "blahblahblah\u20AC\u20AC";
jaroslav@609
    47
 *     byte[] input = inputString.getBytes("UTF-8");
jaroslav@609
    48
 *
jaroslav@609
    49
 *     // Compress the bytes
jaroslav@609
    50
 *     byte[] output = new byte[100];
jaroslav@609
    51
 *     Deflater compresser = new Deflater();
jaroslav@609
    52
 *     compresser.setInput(input);
jaroslav@609
    53
 *     compresser.finish();
jaroslav@609
    54
 *     int compressedDataLength = compresser.deflate(output);
jaroslav@609
    55
 *
jaroslav@609
    56
 *     // Decompress the bytes
jaroslav@609
    57
 *     Inflater decompresser = new Inflater();
jaroslav@609
    58
 *     decompresser.setInput(output, 0, compressedDataLength);
jaroslav@609
    59
 *     byte[] result = new byte[100];
jaroslav@609
    60
 *     int resultLength = decompresser.inflate(result);
jaroslav@609
    61
 *     decompresser.end();
jaroslav@609
    62
 *
jaroslav@609
    63
 *     // Decode the bytes into a String
jaroslav@609
    64
 *     String outputString = new String(result, 0, resultLength, "UTF-8");
jaroslav@609
    65
 * } catch(java.io.UnsupportedEncodingException ex) {
jaroslav@609
    66
 *     // handle
jaroslav@609
    67
 * } catch (java.util.zip.DataFormatException ex) {
jaroslav@609
    68
 *     // handle
jaroslav@609
    69
 * }
jaroslav@609
    70
 * </pre></blockquote>
jaroslav@609
    71
 *
jaroslav@609
    72
 * @see         Deflater
jaroslav@609
    73
 * @author      David Connelly
jaroslav@609
    74
 *
jaroslav@609
    75
 */
jaroslav@694
    76
public
jaroslav@694
    77
class Inflater extends java.util.zip.Inflater {
jaroslav@694
    78
    private final boolean nowrap;
jaroslav@694
    79
    private JzLibInflater impl;
jaroslav@694
    80
    
jaroslav@694
    81
    /**
jaroslav@694
    82
     * Creates a new decompressor. If the parameter 'nowrap' is true then
jaroslav@694
    83
     * the ZLIB header and checksum fields will not be used. This provides
jaroslav@694
    84
     * compatibility with the compression format used by both GZIP and PKZIP.
jaroslav@694
    85
     * <p>
jaroslav@694
    86
     * Note: When using the 'nowrap' option it is also necessary to provide
jaroslav@694
    87
     * an extra "dummy" byte as input. This is required by the ZLIB native
jaroslav@694
    88
     * library in order to support certain optimizations.
jaroslav@694
    89
     *
jaroslav@694
    90
     * @param nowrap if true then support GZIP compatible compression
jaroslav@694
    91
     */
jaroslav@694
    92
    public Inflater(boolean nowrap) {
jaroslav@694
    93
        this.nowrap = nowrap;
jaroslav@694
    94
        reset();
jaroslav@694
    95
    }
jaroslav@609
    96
jaroslav@694
    97
    /**
jaroslav@694
    98
     * Creates a new decompressor.
jaroslav@694
    99
     */
jaroslav@694
   100
    public Inflater() {
jaroslav@694
   101
        this(false);
jaroslav@694
   102
    }
jaroslav@609
   103
jaroslav@694
   104
    /**
jaroslav@694
   105
     * Sets input data for decompression. Should be called whenever
jaroslav@694
   106
     * needsInput() returns true indicating that more input data is
jaroslav@694
   107
     * required.
jaroslav@694
   108
     * @param b the input data bytes
jaroslav@694
   109
     * @param off the start offset of the input data
jaroslav@694
   110
     * @param len the length of the input data
jaroslav@694
   111
     * @see Inflater#needsInput
jaroslav@694
   112
     */
jaroslav@694
   113
    public void setInput(byte[] b, int off, int len) {
jaroslav@694
   114
        if (b == null) {
jaroslav@694
   115
            throw new NullPointerException();
jaroslav@694
   116
        }
jaroslav@694
   117
        if (off < 0 || len < 0 || off > b.length - len) {
jaroslav@694
   118
            throw new ArrayIndexOutOfBoundsException();
jaroslav@694
   119
        }
jaroslav@694
   120
        impl.setInput(b, off, len, false);
jaroslav@694
   121
    }
jaroslav@640
   122
jaroslav@694
   123
    /**
jaroslav@694
   124
     * Sets input data for decompression. Should be called whenever
jaroslav@694
   125
     * needsInput() returns true indicating that more input data is
jaroslav@694
   126
     * required.
jaroslav@694
   127
     * @param b the input data bytes
jaroslav@694
   128
     * @see Inflater#needsInput
jaroslav@694
   129
     */
jaroslav@694
   130
    public void setInput(byte[] b) {
jaroslav@694
   131
        setInput(b, 0, b.length);
jaroslav@694
   132
    }
jaroslav@640
   133
jaroslav@694
   134
    /**
jaroslav@694
   135
     * Sets the preset dictionary to the given array of bytes. Should be
jaroslav@694
   136
     * called when inflate() returns 0 and needsDictionary() returns true
jaroslav@694
   137
     * indicating that a preset dictionary is required. The method getAdler()
jaroslav@694
   138
     * can be used to get the Adler-32 value of the dictionary needed.
jaroslav@694
   139
     * @param b the dictionary data bytes
jaroslav@694
   140
     * @param off the start offset of the data
jaroslav@694
   141
     * @param len the length of the data
jaroslav@694
   142
     * @see Inflater#needsDictionary
jaroslav@694
   143
     * @see Inflater#getAdler
jaroslav@694
   144
     */
jaroslav@694
   145
    public void setDictionary(byte[] b, int off, int len) {
jaroslav@694
   146
        if (b == null) {
jaroslav@694
   147
            throw new NullPointerException();
jaroslav@694
   148
        }
jaroslav@694
   149
        if (off < 0 || len < 0 || off > b.length - len) {
jaroslav@694
   150
            throw new ArrayIndexOutOfBoundsException();
jaroslav@694
   151
        }
jaroslav@694
   152
        byte[] arr;
jaroslav@694
   153
        if (off == 0) {
jaroslav@694
   154
            arr = b;
jaroslav@694
   155
        } else {
jaroslav@694
   156
            arr = new byte[len];
jaroslav@694
   157
            org.apidesign.bck2brwsr.emul.lang.System.arraycopy(b, off, arr, 0, len);
jaroslav@694
   158
        }
jaroslav@694
   159
        impl.setDictionary(arr, len);
jaroslav@694
   160
    }
jaroslav@640
   161
jaroslav@694
   162
    /**
jaroslav@694
   163
     * Sets the preset dictionary to the given array of bytes. Should be
jaroslav@694
   164
     * called when inflate() returns 0 and needsDictionary() returns true
jaroslav@694
   165
     * indicating that a preset dictionary is required. The method getAdler()
jaroslav@694
   166
     * can be used to get the Adler-32 value of the dictionary needed.
jaroslav@694
   167
     * @param b the dictionary data bytes
jaroslav@694
   168
     * @see Inflater#needsDictionary
jaroslav@694
   169
     * @see Inflater#getAdler
jaroslav@694
   170
     */
jaroslav@694
   171
    public void setDictionary(byte[] b) {
jaroslav@694
   172
        impl.setDictionary(b, b.length);
jaroslav@694
   173
    }
jaroslav@640
   174
jaroslav@694
   175
    /**
jaroslav@694
   176
     * Returns the total number of bytes remaining in the input buffer.
jaroslav@694
   177
     * This can be used to find out what bytes still remain in the input
jaroslav@694
   178
     * buffer after decompression has finished.
jaroslav@694
   179
     * @return the total number of bytes remaining in the input buffer
jaroslav@694
   180
     */
jaroslav@694
   181
    public int getRemaining() {
jaroslav@694
   182
        return impl.getAvailIn();
jaroslav@694
   183
    }
jaroslav@640
   184
jaroslav@694
   185
    /**
jaroslav@694
   186
     * Returns true if no data remains in the input buffer. This can
jaroslav@694
   187
     * be used to determine if #setInput should be called in order
jaroslav@694
   188
     * to provide more input.
jaroslav@694
   189
     * @return true if no data remains in the input buffer
jaroslav@694
   190
     */
jaroslav@694
   191
    public boolean needsInput() {
jaroslav@694
   192
        return getRemaining() <= 0;
jaroslav@694
   193
    }
jaroslav@640
   194
jaroslav@694
   195
    /**
jaroslav@694
   196
     * Returns true if a preset dictionary is needed for decompression.
jaroslav@694
   197
     * @return true if a preset dictionary is needed for decompression
jaroslav@694
   198
     * @see Inflater#setDictionary
jaroslav@694
   199
     */
jaroslav@694
   200
    public boolean needsDictionary() {
jaroslav@694
   201
        return impl.needDict();
jaroslav@694
   202
    }
jaroslav@640
   203
jaroslav@694
   204
    /**
jaroslav@694
   205
     * Returns true if the end of the compressed data stream has been
jaroslav@694
   206
     * reached.
jaroslav@694
   207
     * @return true if the end of the compressed data stream has been
jaroslav@694
   208
     * reached
jaroslav@694
   209
     */
jaroslav@694
   210
    public boolean finished() {
jaroslav@694
   211
        return impl.finished();
jaroslav@694
   212
    }
jaroslav@640
   213
jaroslav@694
   214
    /**
jaroslav@694
   215
     * Uncompresses bytes into specified buffer. Returns actual number
jaroslav@694
   216
     * of bytes uncompressed. A return value of 0 indicates that
jaroslav@694
   217
     * needsInput() or needsDictionary() should be called in order to
jaroslav@694
   218
     * determine if more input data or a preset dictionary is required.
jaroslav@694
   219
     * In the latter case, getAdler() can be used to get the Adler-32
jaroslav@694
   220
     * value of the dictionary required.
jaroslav@694
   221
     * @param b the buffer for the uncompressed data
jaroslav@694
   222
     * @param off the start offset of the data
jaroslav@694
   223
     * @param len the maximum number of uncompressed bytes
jaroslav@694
   224
     * @return the actual number of uncompressed bytes
jaroslav@694
   225
     * @exception DataFormatException if the compressed data format is invalid
jaroslav@694
   226
     * @see Inflater#needsInput
jaroslav@694
   227
     * @see Inflater#needsDictionary
jaroslav@694
   228
     */
jaroslav@694
   229
    public int inflate(byte[] b, int off, int len)
jaroslav@694
   230
        throws DataFormatException
jaroslav@694
   231
    {
jaroslav@694
   232
        if (b == null) {
jaroslav@694
   233
            throw new NullPointerException();
jaroslav@694
   234
        }
jaroslav@694
   235
        if (off < 0 || len < 0 || off > b.length - len) {
jaroslav@694
   236
            throw new ArrayIndexOutOfBoundsException();
jaroslav@694
   237
        }
jaroslav@694
   238
        impl.setOutput(b, off, len);
jaroslav@694
   239
        int err = impl.inflate(JzLibInflater.Z_NO_FLUSH);
jaroslav@694
   240
        return impl.next_out_index - off;
jaroslav@694
   241
    }
jaroslav@640
   242
jaroslav@694
   243
    /**
jaroslav@694
   244
     * Uncompresses bytes into specified buffer. Returns actual number
jaroslav@694
   245
     * of bytes uncompressed. A return value of 0 indicates that
jaroslav@694
   246
     * needsInput() or needsDictionary() should be called in order to
jaroslav@694
   247
     * determine if more input data or a preset dictionary is required.
jaroslav@694
   248
     * In the latter case, getAdler() can be used to get the Adler-32
jaroslav@694
   249
     * value of the dictionary required.
jaroslav@694
   250
     * @param b the buffer for the uncompressed data
jaroslav@694
   251
     * @return the actual number of uncompressed bytes
jaroslav@694
   252
     * @exception DataFormatException if the compressed data format is invalid
jaroslav@694
   253
     * @see Inflater#needsInput
jaroslav@694
   254
     * @see Inflater#needsDictionary
jaroslav@694
   255
     */
jaroslav@694
   256
    public int inflate(byte[] b) throws DataFormatException {
jaroslav@694
   257
        return inflate(b, 0, b.length);
jaroslav@694
   258
    }
jaroslav@640
   259
jaroslav@694
   260
    /**
jaroslav@694
   261
     * Returns the ADLER-32 value of the uncompressed data.
jaroslav@694
   262
     * @return the ADLER-32 value of the uncompressed data
jaroslav@694
   263
     */
jaroslav@694
   264
    public int getAdler() {
jaroslav@694
   265
        return (int) impl.getAdler();
jaroslav@694
   266
    }
jaroslav@640
   267
jaroslav@694
   268
    /**
jaroslav@694
   269
     * Returns the total number of compressed bytes input so far.
jaroslav@694
   270
     *
jaroslav@694
   271
     * <p>Since the number of bytes may be greater than
jaroslav@694
   272
     * Integer.MAX_VALUE, the {@link #getBytesRead()} method is now
jaroslav@694
   273
     * the preferred means of obtaining this information.</p>
jaroslav@694
   274
     *
jaroslav@694
   275
     * @return the total number of compressed bytes input so far
jaroslav@694
   276
     */
jaroslav@694
   277
    public int getTotalIn() {
jaroslav@694
   278
        return (int) getBytesRead();
jaroslav@694
   279
    }
jaroslav@640
   280
jaroslav@694
   281
    /**
jaroslav@694
   282
     * Returns the total number of compressed bytes input so far.</p>
jaroslav@694
   283
     *
jaroslav@694
   284
     * @return the total (non-negative) number of compressed bytes input so far
jaroslav@694
   285
     * @since 1.5
jaroslav@694
   286
     */
jaroslav@694
   287
    public long getBytesRead() {
jaroslav@694
   288
        return impl.total_in;
jaroslav@694
   289
    }
jaroslav@640
   290
jaroslav@694
   291
    /**
jaroslav@694
   292
     * Returns the total number of uncompressed bytes output so far.
jaroslav@694
   293
     *
jaroslav@694
   294
     * <p>Since the number of bytes may be greater than
jaroslav@694
   295
     * Integer.MAX_VALUE, the {@link #getBytesWritten()} method is now
jaroslav@694
   296
     * the preferred means of obtaining this information.</p>
jaroslav@694
   297
     *
jaroslav@694
   298
     * @return the total number of uncompressed bytes output so far
jaroslav@694
   299
     */
jaroslav@694
   300
    public int getTotalOut() {
jaroslav@694
   301
        return (int) getBytesWritten();
jaroslav@694
   302
    }
jaroslav@640
   303
jaroslav@694
   304
    /**
jaroslav@694
   305
     * Returns the total number of uncompressed bytes output so far.</p>
jaroslav@694
   306
     *
jaroslav@694
   307
     * @return the total (non-negative) number of uncompressed bytes output so far
jaroslav@694
   308
     * @since 1.5
jaroslav@694
   309
     */
jaroslav@694
   310
    public long getBytesWritten() {
jaroslav@694
   311
        return impl.total_out;
jaroslav@694
   312
    }
jaroslav@640
   313
jaroslav@694
   314
    /**
jaroslav@694
   315
     * Resets inflater so that a new set of input data can be processed.
jaroslav@694
   316
     */
jaroslav@694
   317
    public void reset() {
jaroslav@694
   318
        impl = new JzLibInflater(15, nowrap);
jaroslav@694
   319
    }
jaroslav@640
   320
jaroslav@694
   321
    /**
jaroslav@694
   322
     * Closes the decompressor and discards any unprocessed input.
jaroslav@694
   323
     * This method should be called when the decompressor is no longer
jaroslav@694
   324
     * being used, but will also be called automatically by the finalize()
jaroslav@694
   325
     * method. Once this method is called, the behavior of the Inflater
jaroslav@694
   326
     * object is undefined.
jaroslav@694
   327
     */
jaroslav@694
   328
    public void end() {
jaroslav@694
   329
        impl.end();
jaroslav@694
   330
    }
jaroslav@640
   331
jaroslav@694
   332
    /**
jaroslav@694
   333
     * Closes the decompressor when garbage is collected.
jaroslav@640
   334
     */
jaroslav@694
   335
    protected void finalize() {
jaroslav@694
   336
        end();
jaroslav@640
   337
    }
jaroslav@609
   338
}