emul/mini/src/main/java/java/util/zip/InflaterInputStream.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Thu, 07 Feb 2013 12:58:12 +0100
branchemul
changeset 694 0d277415ed02
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@609
     1
/*
jaroslav@609
     2
 * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved.
jaroslav@609
     3
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
jaroslav@609
     4
 *
jaroslav@609
     5
 * This code is free software; you can redistribute it and/or modify it
jaroslav@609
     6
 * under the terms of the GNU General Public License version 2 only, as
jaroslav@609
     7
 * published by the Free Software Foundation.  Oracle designates this
jaroslav@609
     8
 * particular file as subject to the "Classpath" exception as provided
jaroslav@609
     9
 * by Oracle in the LICENSE file that accompanied this code.
jaroslav@609
    10
 *
jaroslav@609
    11
 * This code is distributed in the hope that it will be useful, but WITHOUT
jaroslav@609
    12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
jaroslav@609
    13
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
jaroslav@609
    14
 * version 2 for more details (a copy is included in the LICENSE file that
jaroslav@609
    15
 * accompanied this code).
jaroslav@609
    16
 *
jaroslav@609
    17
 * You should have received a copy of the GNU General Public License version
jaroslav@609
    18
 * 2 along with this work; if not, write to the Free Software Foundation,
jaroslav@609
    19
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
jaroslav@609
    20
 *
jaroslav@609
    21
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
jaroslav@609
    22
 * or visit www.oracle.com if you need additional information or have any
jaroslav@609
    23
 * questions.
jaroslav@609
    24
 */
jaroslav@609
    25
jaroslav@609
    26
package java.util.zip;
jaroslav@609
    27
jaroslav@609
    28
import java.io.FilterInputStream;
jaroslav@609
    29
import java.io.InputStream;
jaroslav@609
    30
import java.io.IOException;
jaroslav@609
    31
import java.io.EOFException;
jaroslav@609
    32
jaroslav@609
    33
/**
jaroslav@609
    34
 * This class implements a stream filter for uncompressing data in the
jaroslav@609
    35
 * "deflate" compression format. It is also used as the basis for other
jaroslav@609
    36
 * decompression filters, such as GZIPInputStream.
jaroslav@609
    37
 *
jaroslav@609
    38
 * @see         Inflater
jaroslav@609
    39
 * @author      David Connelly
jaroslav@609
    40
 */
jaroslav@609
    41
public
jaroslav@609
    42
class InflaterInputStream extends FilterInputStream {
jaroslav@609
    43
    /**
jaroslav@609
    44
     * Decompressor for this stream.
jaroslav@609
    45
     */
jaroslav@609
    46
    protected Inflater inf;
jaroslav@609
    47
jaroslav@609
    48
    /**
jaroslav@609
    49
     * Input buffer for decompression.
jaroslav@609
    50
     */
jaroslav@609
    51
    protected byte[] buf;
jaroslav@609
    52
jaroslav@609
    53
    /**
jaroslav@609
    54
     * Length of input buffer.
jaroslav@609
    55
     */
jaroslav@609
    56
    protected int len;
jaroslav@609
    57
jaroslav@609
    58
    private boolean closed = false;
jaroslav@609
    59
    // this flag is set to true after EOF has reached
jaroslav@609
    60
    private boolean reachEOF = false;
jaroslav@609
    61
jaroslav@609
    62
    /**
jaroslav@609
    63
     * Check to make sure that this stream has not been closed
jaroslav@609
    64
     */
jaroslav@609
    65
    private void ensureOpen() throws IOException {
jaroslav@609
    66
        if (closed) {
jaroslav@609
    67
            throw new IOException("Stream closed");
jaroslav@609
    68
        }
jaroslav@609
    69
    }
jaroslav@609
    70
jaroslav@609
    71
jaroslav@609
    72
    /**
jaroslav@609
    73
     * Creates a new input stream with the specified decompressor and
jaroslav@609
    74
     * buffer size.
jaroslav@609
    75
     * @param in the input stream
jaroslav@609
    76
     * @param inf the decompressor ("inflater")
jaroslav@609
    77
     * @param size the input buffer size
jaroslav@609
    78
     * @exception IllegalArgumentException if size is <= 0
jaroslav@609
    79
     */
jaroslav@609
    80
    public InflaterInputStream(InputStream in, Inflater inf, int size) {
jaroslav@609
    81
        super(in);
jaroslav@609
    82
        if (in == null || inf == null) {
jaroslav@609
    83
            throw new NullPointerException();
jaroslav@609
    84
        } else if (size <= 0) {
jaroslav@609
    85
            throw new IllegalArgumentException("buffer size <= 0");
jaroslav@609
    86
        }
jaroslav@609
    87
        this.inf = inf;
jaroslav@609
    88
        buf = new byte[size];
jaroslav@609
    89
    }
jaroslav@609
    90
jaroslav@609
    91
    /**
jaroslav@609
    92
     * Creates a new input stream with the specified decompressor and a
jaroslav@609
    93
     * default buffer size.
jaroslav@609
    94
     * @param in the input stream
jaroslav@609
    95
     * @param inf the decompressor ("inflater")
jaroslav@609
    96
     */
jaroslav@609
    97
    public InflaterInputStream(InputStream in, Inflater inf) {
jaroslav@609
    98
        this(in, inf, 512);
jaroslav@609
    99
    }
jaroslav@609
   100
jaroslav@609
   101
    boolean usesDefaultInflater = false;
jaroslav@609
   102
jaroslav@609
   103
    /**
jaroslav@609
   104
     * Creates a new input stream with a default decompressor and buffer size.
jaroslav@609
   105
     * @param in the input stream
jaroslav@609
   106
     */
jaroslav@609
   107
    public InflaterInputStream(InputStream in) {
jaroslav@609
   108
        this(in, new Inflater());
jaroslav@609
   109
        usesDefaultInflater = true;
jaroslav@609
   110
    }
jaroslav@609
   111
jaroslav@609
   112
    private byte[] singleByteBuf = new byte[1];
jaroslav@609
   113
jaroslav@609
   114
    /**
jaroslav@609
   115
     * Reads a byte of uncompressed data. This method will block until
jaroslav@609
   116
     * enough input is available for decompression.
jaroslav@609
   117
     * @return the byte read, or -1 if end of compressed input is reached
jaroslav@609
   118
     * @exception IOException if an I/O error has occurred
jaroslav@609
   119
     */
jaroslav@609
   120
    public int read() throws IOException {
jaroslav@609
   121
        ensureOpen();
jaroslav@609
   122
        return read(singleByteBuf, 0, 1) == -1 ? -1 : singleByteBuf[0] & 0xff;
jaroslav@609
   123
    }
jaroslav@609
   124
jaroslav@609
   125
    /**
jaroslav@609
   126
     * Reads uncompressed data into an array of bytes. If <code>len</code> is not
jaroslav@609
   127
     * zero, the method will block until some input can be decompressed; otherwise,
jaroslav@609
   128
     * no bytes are read and <code>0</code> is returned.
jaroslav@609
   129
     * @param b the buffer into which the data is read
jaroslav@609
   130
     * @param off the start offset in the destination array <code>b</code>
jaroslav@609
   131
     * @param len the maximum number of bytes read
jaroslav@609
   132
     * @return the actual number of bytes read, or -1 if the end of the
jaroslav@609
   133
     *         compressed input is reached or a preset dictionary is needed
jaroslav@609
   134
     * @exception  NullPointerException If <code>b</code> is <code>null</code>.
jaroslav@609
   135
     * @exception  IndexOutOfBoundsException If <code>off</code> is negative,
jaroslav@609
   136
     * <code>len</code> is negative, or <code>len</code> is greater than
jaroslav@609
   137
     * <code>b.length - off</code>
jaroslav@609
   138
     * @exception ZipException if a ZIP format error has occurred
jaroslav@609
   139
     * @exception IOException if an I/O error has occurred
jaroslav@609
   140
     */
jaroslav@609
   141
    public int read(byte[] b, int off, int len) throws IOException {
jaroslav@609
   142
        ensureOpen();
jaroslav@609
   143
        if (b == null) {
jaroslav@609
   144
            throw new NullPointerException();
jaroslav@609
   145
        } else if (off < 0 || len < 0 || len > b.length - off) {
jaroslav@609
   146
            throw new IndexOutOfBoundsException();
jaroslav@609
   147
        } else if (len == 0) {
jaroslav@609
   148
            return 0;
jaroslav@609
   149
        }
jaroslav@609
   150
        try {
jaroslav@609
   151
            int n;
jaroslav@609
   152
            while ((n = inf.inflate(b, off, len)) == 0) {
jaroslav@609
   153
                if (inf.finished() || inf.needsDictionary()) {
jaroslav@609
   154
                    reachEOF = true;
jaroslav@609
   155
                    return -1;
jaroslav@609
   156
                }
jaroslav@609
   157
                if (inf.needsInput()) {
jaroslav@609
   158
                    fill();
jaroslav@609
   159
                }
jaroslav@609
   160
            }
jaroslav@609
   161
            return n;
jaroslav@609
   162
        } catch (DataFormatException e) {
jaroslav@609
   163
            String s = e.getMessage();
jaroslav@609
   164
            throw new ZipException(s != null ? s : "Invalid ZLIB data format");
jaroslav@609
   165
        }
jaroslav@609
   166
    }
jaroslav@609
   167
jaroslav@609
   168
    /**
jaroslav@609
   169
     * Returns 0 after EOF has been reached, otherwise always return 1.
jaroslav@609
   170
     * <p>
jaroslav@609
   171
     * Programs should not count on this method to return the actual number
jaroslav@609
   172
     * of bytes that could be read without blocking.
jaroslav@609
   173
     *
jaroslav@609
   174
     * @return     1 before EOF and 0 after EOF.
jaroslav@609
   175
     * @exception  IOException  if an I/O error occurs.
jaroslav@609
   176
     *
jaroslav@609
   177
     */
jaroslav@609
   178
    public int available() throws IOException {
jaroslav@609
   179
        ensureOpen();
jaroslav@609
   180
        if (reachEOF) {
jaroslav@609
   181
            return 0;
jaroslav@609
   182
        } else {
jaroslav@609
   183
            return 1;
jaroslav@609
   184
        }
jaroslav@609
   185
    }
jaroslav@609
   186
jaroslav@609
   187
    private byte[] b = new byte[512];
jaroslav@609
   188
jaroslav@609
   189
    /**
jaroslav@609
   190
     * Skips specified number of bytes of uncompressed data.
jaroslav@609
   191
     * @param n the number of bytes to skip
jaroslav@609
   192
     * @return the actual number of bytes skipped.
jaroslav@609
   193
     * @exception IOException if an I/O error has occurred
jaroslav@609
   194
     * @exception IllegalArgumentException if n < 0
jaroslav@609
   195
     */
jaroslav@609
   196
    public long skip(long n) throws IOException {
jaroslav@609
   197
        if (n < 0) {
jaroslav@609
   198
            throw new IllegalArgumentException("negative skip length");
jaroslav@609
   199
        }
jaroslav@609
   200
        ensureOpen();
jaroslav@609
   201
        int max = (int)Math.min(n, Integer.MAX_VALUE);
jaroslav@609
   202
        int total = 0;
jaroslav@609
   203
        while (total < max) {
jaroslav@609
   204
            int len = max - total;
jaroslav@609
   205
            if (len > b.length) {
jaroslav@609
   206
                len = b.length;
jaroslav@609
   207
            }
jaroslav@609
   208
            len = read(b, 0, len);
jaroslav@609
   209
            if (len == -1) {
jaroslav@609
   210
                reachEOF = true;
jaroslav@609
   211
                break;
jaroslav@609
   212
            }
jaroslav@609
   213
            total += len;
jaroslav@609
   214
        }
jaroslav@609
   215
        return total;
jaroslav@609
   216
    }
jaroslav@609
   217
jaroslav@609
   218
    /**
jaroslav@609
   219
     * Closes this input stream and releases any system resources associated
jaroslav@609
   220
     * with the stream.
jaroslav@609
   221
     * @exception IOException if an I/O error has occurred
jaroslav@609
   222
     */
jaroslav@609
   223
    public void close() throws IOException {
jaroslav@609
   224
        if (!closed) {
jaroslav@609
   225
            if (usesDefaultInflater)
jaroslav@609
   226
                inf.end();
jaroslav@609
   227
            in.close();
jaroslav@609
   228
            closed = true;
jaroslav@609
   229
        }
jaroslav@609
   230
    }
jaroslav@609
   231
jaroslav@609
   232
    /**
jaroslav@609
   233
     * Fills input buffer with more data to decompress.
jaroslav@609
   234
     * @exception IOException if an I/O error has occurred
jaroslav@609
   235
     */
jaroslav@609
   236
    protected void fill() throws IOException {
jaroslav@609
   237
        ensureOpen();
jaroslav@609
   238
        len = in.read(buf, 0, buf.length);
jaroslav@609
   239
        if (len == -1) {
jaroslav@609
   240
            throw new EOFException("Unexpected end of ZLIB input stream");
jaroslav@609
   241
        }
jaroslav@609
   242
        inf.setInput(buf, 0, len);
jaroslav@609
   243
    }
jaroslav@609
   244
jaroslav@609
   245
    /**
jaroslav@609
   246
     * Tests if this input stream supports the <code>mark</code> and
jaroslav@609
   247
     * <code>reset</code> methods. The <code>markSupported</code>
jaroslav@609
   248
     * method of <code>InflaterInputStream</code> returns
jaroslav@609
   249
     * <code>false</code>.
jaroslav@609
   250
     *
jaroslav@609
   251
     * @return  a <code>boolean</code> indicating if this stream type supports
jaroslav@609
   252
     *          the <code>mark</code> and <code>reset</code> methods.
jaroslav@609
   253
     * @see     java.io.InputStream#mark(int)
jaroslav@609
   254
     * @see     java.io.InputStream#reset()
jaroslav@609
   255
     */
jaroslav@609
   256
    public boolean markSupported() {
jaroslav@609
   257
        return false;
jaroslav@609
   258
    }
jaroslav@609
   259
jaroslav@609
   260
    /**
jaroslav@609
   261
     * Marks the current position in this input stream.
jaroslav@609
   262
     *
jaroslav@609
   263
     * <p> The <code>mark</code> method of <code>InflaterInputStream</code>
jaroslav@609
   264
     * does nothing.
jaroslav@609
   265
     *
jaroslav@609
   266
     * @param   readlimit   the maximum limit of bytes that can be read before
jaroslav@609
   267
     *                      the mark position becomes invalid.
jaroslav@609
   268
     * @see     java.io.InputStream#reset()
jaroslav@609
   269
     */
jaroslav@609
   270
    public synchronized void mark(int readlimit) {
jaroslav@609
   271
    }
jaroslav@609
   272
jaroslav@609
   273
    /**
jaroslav@609
   274
     * Repositions this stream to the position at the time the
jaroslav@609
   275
     * <code>mark</code> method was last called on this input stream.
jaroslav@609
   276
     *
jaroslav@609
   277
     * <p> The method <code>reset</code> for class
jaroslav@609
   278
     * <code>InflaterInputStream</code> does nothing except throw an
jaroslav@609
   279
     * <code>IOException</code>.
jaroslav@609
   280
     *
jaroslav@609
   281
     * @exception  IOException  if this method is invoked.
jaroslav@609
   282
     * @see     java.io.InputStream#mark(int)
jaroslav@609
   283
     * @see     java.io.IOException
jaroslav@609
   284
     */
jaroslav@609
   285
    public synchronized void reset() throws IOException {
jaroslav@609
   286
        throw new IOException("mark/reset not supported");
jaroslav@609
   287
    }
jaroslav@609
   288
}