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.
     1 /*
     2  * Copyright (c) 1996, 2006, 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.io.FilterInputStream;
    29 import java.io.InputStream;
    30 import java.io.IOException;
    31 import java.io.EOFException;
    32 
    33 /**
    34  * This class implements a stream filter for uncompressing data in the
    35  * "deflate" compression format. It is also used as the basis for other
    36  * decompression filters, such as GZIPInputStream.
    37  *
    38  * @see         Inflater
    39  * @author      David Connelly
    40  */
    41 public
    42 class InflaterInputStream extends FilterInputStream {
    43     /**
    44      * Decompressor for this stream.
    45      */
    46     protected Inflater inf;
    47 
    48     /**
    49      * Input buffer for decompression.
    50      */
    51     protected byte[] buf;
    52 
    53     /**
    54      * Length of input buffer.
    55      */
    56     protected int len;
    57 
    58     private boolean closed = false;
    59     // this flag is set to true after EOF has reached
    60     private boolean reachEOF = false;
    61 
    62     /**
    63      * Check to make sure that this stream has not been closed
    64      */
    65     private void ensureOpen() throws IOException {
    66         if (closed) {
    67             throw new IOException("Stream closed");
    68         }
    69     }
    70 
    71 
    72     /**
    73      * Creates a new input stream with the specified decompressor and
    74      * buffer size.
    75      * @param in the input stream
    76      * @param inf the decompressor ("inflater")
    77      * @param size the input buffer size
    78      * @exception IllegalArgumentException if size is <= 0
    79      */
    80     public InflaterInputStream(InputStream in, Inflater inf, int size) {
    81         super(in);
    82         if (in == null || inf == null) {
    83             throw new NullPointerException();
    84         } else if (size <= 0) {
    85             throw new IllegalArgumentException("buffer size <= 0");
    86         }
    87         this.inf = inf;
    88         buf = new byte[size];
    89     }
    90 
    91     /**
    92      * Creates a new input stream with the specified decompressor and a
    93      * default buffer size.
    94      * @param in the input stream
    95      * @param inf the decompressor ("inflater")
    96      */
    97     public InflaterInputStream(InputStream in, Inflater inf) {
    98         this(in, inf, 512);
    99     }
   100 
   101     boolean usesDefaultInflater = false;
   102 
   103     /**
   104      * Creates a new input stream with a default decompressor and buffer size.
   105      * @param in the input stream
   106      */
   107     public InflaterInputStream(InputStream in) {
   108         this(in, new Inflater());
   109         usesDefaultInflater = true;
   110     }
   111 
   112     private byte[] singleByteBuf = new byte[1];
   113 
   114     /**
   115      * Reads a byte of uncompressed data. This method will block until
   116      * enough input is available for decompression.
   117      * @return the byte read, or -1 if end of compressed input is reached
   118      * @exception IOException if an I/O error has occurred
   119      */
   120     public int read() throws IOException {
   121         ensureOpen();
   122         return read(singleByteBuf, 0, 1) == -1 ? -1 : singleByteBuf[0] & 0xff;
   123     }
   124 
   125     /**
   126      * Reads uncompressed data into an array of bytes. If <code>len</code> is not
   127      * zero, the method will block until some input can be decompressed; otherwise,
   128      * no bytes are read and <code>0</code> is returned.
   129      * @param b the buffer into which the data is read
   130      * @param off the start offset in the destination array <code>b</code>
   131      * @param len the maximum number of bytes read
   132      * @return the actual number of bytes read, or -1 if the end of the
   133      *         compressed input is reached or a preset dictionary is needed
   134      * @exception  NullPointerException If <code>b</code> is <code>null</code>.
   135      * @exception  IndexOutOfBoundsException If <code>off</code> is negative,
   136      * <code>len</code> is negative, or <code>len</code> is greater than
   137      * <code>b.length - off</code>
   138      * @exception ZipException if a ZIP format error has occurred
   139      * @exception IOException if an I/O error has occurred
   140      */
   141     public int read(byte[] b, int off, int len) throws IOException {
   142         ensureOpen();
   143         if (b == null) {
   144             throw new NullPointerException();
   145         } else if (off < 0 || len < 0 || len > b.length - off) {
   146             throw new IndexOutOfBoundsException();
   147         } else if (len == 0) {
   148             return 0;
   149         }
   150         try {
   151             int n;
   152             while ((n = inf.inflate(b, off, len)) == 0) {
   153                 if (inf.finished() || inf.needsDictionary()) {
   154                     reachEOF = true;
   155                     return -1;
   156                 }
   157                 if (inf.needsInput()) {
   158                     fill();
   159                 }
   160             }
   161             return n;
   162         } catch (DataFormatException e) {
   163             String s = e.getMessage();
   164             throw new ZipException(s != null ? s : "Invalid ZLIB data format");
   165         }
   166     }
   167 
   168     /**
   169      * Returns 0 after EOF has been reached, otherwise always return 1.
   170      * <p>
   171      * Programs should not count on this method to return the actual number
   172      * of bytes that could be read without blocking.
   173      *
   174      * @return     1 before EOF and 0 after EOF.
   175      * @exception  IOException  if an I/O error occurs.
   176      *
   177      */
   178     public int available() throws IOException {
   179         ensureOpen();
   180         if (reachEOF) {
   181             return 0;
   182         } else {
   183             return 1;
   184         }
   185     }
   186 
   187     private byte[] b = new byte[512];
   188 
   189     /**
   190      * Skips specified number of bytes of uncompressed data.
   191      * @param n the number of bytes to skip
   192      * @return the actual number of bytes skipped.
   193      * @exception IOException if an I/O error has occurred
   194      * @exception IllegalArgumentException if n < 0
   195      */
   196     public long skip(long n) throws IOException {
   197         if (n < 0) {
   198             throw new IllegalArgumentException("negative skip length");
   199         }
   200         ensureOpen();
   201         int max = (int)Math.min(n, Integer.MAX_VALUE);
   202         int total = 0;
   203         while (total < max) {
   204             int len = max - total;
   205             if (len > b.length) {
   206                 len = b.length;
   207             }
   208             len = read(b, 0, len);
   209             if (len == -1) {
   210                 reachEOF = true;
   211                 break;
   212             }
   213             total += len;
   214         }
   215         return total;
   216     }
   217 
   218     /**
   219      * Closes this input stream and releases any system resources associated
   220      * with the stream.
   221      * @exception IOException if an I/O error has occurred
   222      */
   223     public void close() throws IOException {
   224         if (!closed) {
   225             if (usesDefaultInflater)
   226                 inf.end();
   227             in.close();
   228             closed = true;
   229         }
   230     }
   231 
   232     /**
   233      * Fills input buffer with more data to decompress.
   234      * @exception IOException if an I/O error has occurred
   235      */
   236     protected void fill() throws IOException {
   237         ensureOpen();
   238         len = in.read(buf, 0, buf.length);
   239         if (len == -1) {
   240             throw new EOFException("Unexpected end of ZLIB input stream");
   241         }
   242         inf.setInput(buf, 0, len);
   243     }
   244 
   245     /**
   246      * Tests if this input stream supports the <code>mark</code> and
   247      * <code>reset</code> methods. The <code>markSupported</code>
   248      * method of <code>InflaterInputStream</code> returns
   249      * <code>false</code>.
   250      *
   251      * @return  a <code>boolean</code> indicating if this stream type supports
   252      *          the <code>mark</code> and <code>reset</code> methods.
   253      * @see     java.io.InputStream#mark(int)
   254      * @see     java.io.InputStream#reset()
   255      */
   256     public boolean markSupported() {
   257         return false;
   258     }
   259 
   260     /**
   261      * Marks the current position in this input stream.
   262      *
   263      * <p> The <code>mark</code> method of <code>InflaterInputStream</code>
   264      * does nothing.
   265      *
   266      * @param   readlimit   the maximum limit of bytes that can be read before
   267      *                      the mark position becomes invalid.
   268      * @see     java.io.InputStream#reset()
   269      */
   270     public synchronized void mark(int readlimit) {
   271     }
   272 
   273     /**
   274      * Repositions this stream to the position at the time the
   275      * <code>mark</code> method was last called on this input stream.
   276      *
   277      * <p> The method <code>reset</code> for class
   278      * <code>InflaterInputStream</code> does nothing except throw an
   279      * <code>IOException</code>.
   280      *
   281      * @exception  IOException  if this method is invoked.
   282      * @see     java.io.InputStream#mark(int)
   283      * @see     java.io.IOException
   284      */
   285     public synchronized void reset() throws IOException {
   286         throw new IOException("mark/reset not supported");
   287     }
   288 }