Home | History | Annotate | Download | only in zip
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
      4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      5  *
      6  * This code is free software; you can redistribute it and/or modify it
      7  * under the terms of the GNU General Public License version 2 only, as
      8  * published by the Free Software Foundation.  Oracle designates this
      9  * particular file as subject to the "Classpath" exception as provided
     10  * by Oracle in the LICENSE file that accompanied this code.
     11  *
     12  * This code is distributed in the hope that it will be useful, but WITHOUT
     13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     15  * version 2 for more details (a copy is included in the LICENSE file that
     16  * accompanied this code).
     17  *
     18  * You should have received a copy of the GNU General Public License version
     19  * 2 along with this work; if not, write to the Free Software Foundation,
     20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     21  *
     22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     23  * or visit www.oracle.com if you need additional information or have any
     24  * questions.
     25  */
     26 
     27 package java.util.zip;
     28 
     29 import java.io.FilterInputStream;
     30 import java.io.InputStream;
     31 import java.io.IOException;
     32 
     33 /**
     34  * Implements an input stream filter for compressing data in the "deflate"
     35  * compression format.
     36  *
     37  * @since       1.6
     38  * @author      David R Tribble (david (at) tribble.com)
     39  *
     40  * @see DeflaterOutputStream
     41  * @see InflaterOutputStream
     42  * @see InflaterInputStream
     43  */
     44 
     45 public class DeflaterInputStream extends FilterInputStream {
     46     /** Compressor for this stream. */
     47     protected final Deflater def;
     48 
     49     /** Input buffer for reading compressed data. */
     50     protected final byte[] buf;
     51 
     52     /** Temporary read buffer. */
     53     private byte[] rbuf = new byte[1];
     54 
     55     /** Default compressor is used. */
     56     private boolean usesDefaultDeflater = false;
     57 
     58     /** End of the underlying input stream has been reached. */
     59     private boolean reachEOF = false;
     60 
     61     /**
     62      * Check to make sure that this stream has not been closed.
     63      */
     64     private void ensureOpen() throws IOException {
     65         if (in == null) {
     66             throw new IOException("Stream closed");
     67         }
     68     }
     69 
     70     /**
     71      * Creates a new input stream with a default compressor and buffer
     72      * size.
     73      *
     74      * @param in input stream to read the uncompressed data to
     75      * @throws NullPointerException if {@code in} is null
     76      */
     77     public DeflaterInputStream(InputStream in) {
     78         this(in, new Deflater());
     79         usesDefaultDeflater = true;
     80     }
     81 
     82     /**
     83      * Creates a new input stream with the specified compressor and a
     84      * default buffer size.
     85      *
     86      * @param in input stream to read the uncompressed data to
     87      * @param defl compressor ("deflater") for this stream
     88      * @throws NullPointerException if {@code in} or {@code defl} is null
     89      */
     90     public DeflaterInputStream(InputStream in, Deflater defl) {
     91         this(in, defl, 512);
     92     }
     93 
     94     /**
     95      * Creates a new input stream with the specified compressor and buffer
     96      * size.
     97      *
     98      * @param in input stream to read the uncompressed data to
     99      * @param defl compressor ("deflater") for this stream
    100      * @param bufLen compression buffer size
    101      * @throws IllegalArgumentException if {@code bufLen <= 0}
    102      * @throws NullPointerException if {@code in} or {@code defl} is null
    103      */
    104     public DeflaterInputStream(InputStream in, Deflater defl, int bufLen) {
    105         super(in);
    106 
    107         // Sanity checks
    108         if (in == null)
    109             throw new NullPointerException("Null input");
    110         if (defl == null)
    111             throw new NullPointerException("Null deflater");
    112         if (bufLen < 1)
    113             throw new IllegalArgumentException("Buffer size < 1");
    114 
    115         // Initialize
    116         def = defl;
    117         buf = new byte[bufLen];
    118     }
    119 
    120     /**
    121      * Closes this input stream and its underlying input stream, discarding
    122      * any pending uncompressed data.
    123      *
    124      * @throws IOException if an I/O error occurs
    125      */
    126     public void close() throws IOException {
    127         if (in != null) {
    128             try {
    129                 // Clean up
    130                 if (usesDefaultDeflater) {
    131                     def.end();
    132                 }
    133 
    134                 in.close();
    135             } finally {
    136                 in = null;
    137             }
    138         }
    139     }
    140 
    141     /**
    142      * Reads a single byte of compressed data from the input stream.
    143      * This method will block until some input can be read and compressed.
    144      *
    145      * @return a single byte of compressed data, or -1 if the end of the
    146      * uncompressed input stream is reached
    147      * @throws IOException if an I/O error occurs or if this stream is
    148      * already closed
    149      */
    150     public int read() throws IOException {
    151         // Read a single byte of compressed data
    152         int len = read(rbuf, 0, 1);
    153         if (len <= 0)
    154             return -1;
    155         return (rbuf[0] & 0xFF);
    156     }
    157 
    158     /**
    159      * Reads compressed data into a byte array.
    160      * This method will block until some input can be read and compressed.
    161      *
    162      * @param b buffer into which the data is read
    163      * @param off starting offset of the data within {@code b}
    164      * @param len maximum number of compressed bytes to read into {@code b}
    165      * @return the actual number of bytes read, or -1 if the end of the
    166      * uncompressed input stream is reached
    167      * @throws IndexOutOfBoundsException  if {@code len > b.length - off}
    168      * @throws IOException if an I/O error occurs or if this input stream is
    169      * already closed
    170      */
    171     public int read(byte[] b, int off, int len) throws IOException {
    172         // Sanity checks
    173         ensureOpen();
    174         if (b == null) {
    175             throw new NullPointerException("Null buffer for read");
    176         } else if (off < 0 || len < 0 || len > b.length - off) {
    177             throw new IndexOutOfBoundsException();
    178         } else if (len == 0) {
    179             return 0;
    180         }
    181 
    182         // Read and compress (deflate) input data bytes
    183         int cnt = 0;
    184         while (len > 0 && !def.finished()) {
    185             int n;
    186 
    187             // Read data from the input stream
    188             if (def.needsInput()) {
    189                 n = in.read(buf, 0, buf.length);
    190                 if (n < 0) {
    191                     // End of the input stream reached
    192                     def.finish();
    193                 } else if (n > 0) {
    194                     def.setInput(buf, 0, n);
    195                 }
    196             }
    197 
    198             // Compress the input data, filling the read buffer
    199             n = def.deflate(b, off, len);
    200             cnt += n;
    201             off += n;
    202             len -= n;
    203         }
    204         // Android-changed: set reachEOF eagerly (not just when the number of bytes is zero).
    205         // so that available is more accurate.
    206         if (def.finished()) {
    207             reachEOF =true;
    208             if (cnt == 0) {
    209                 cnt = -1;
    210             }
    211         }
    212 
    213         return cnt;
    214     }
    215 
    216     /**
    217      * Skips over and discards data from the input stream.
    218      * This method may block until the specified number of bytes are read and
    219      * skipped. <em>Note:</em> While {@code n} is given as a {@code long},
    220      * the maximum number of bytes which can be skipped is
    221      * {@code Integer.MAX_VALUE}.
    222      *
    223      * @param n number of bytes to be skipped
    224      * @return the actual number of bytes skipped
    225      * @throws IOException if an I/O error occurs or if this stream is
    226      * already closed
    227      */
    228     public long skip(long n) throws IOException {
    229         if (n < 0) {
    230             throw new IllegalArgumentException("negative skip length");
    231         }
    232         ensureOpen();
    233 
    234         // Skip bytes by repeatedly decompressing small blocks
    235         if (rbuf.length < 512)
    236             rbuf = new byte[512];
    237 
    238         int total = (int)Math.min(n, Integer.MAX_VALUE);
    239         long cnt = 0;
    240         while (total > 0) {
    241             // Read a small block of uncompressed bytes
    242             int len = read(rbuf, 0, (total <= rbuf.length ? total : rbuf.length));
    243 
    244             if (len < 0) {
    245                 break;
    246             }
    247             cnt += len;
    248             total -= len;
    249         }
    250         return cnt;
    251     }
    252 
    253     /**
    254      * Returns 0 after EOF has been reached, otherwise always return 1.
    255      * <p>
    256      * Programs should not count on this method to return the actual number
    257      * of bytes that could be read without blocking
    258      * @return zero after the end of the underlying input stream has been
    259      * reached, otherwise always returns 1
    260      * @throws IOException if an I/O error occurs or if this stream is
    261      * already closed
    262      */
    263     public int available() throws IOException {
    264         ensureOpen();
    265         if (reachEOF) {
    266             return 0;
    267         }
    268         return 1;
    269     }
    270 
    271     /**
    272      * Always returns {@code false} because this input stream does not support
    273      * the {@link #mark mark()} and {@link #reset reset()} methods.
    274      *
    275      * @return false, always
    276      */
    277     public boolean markSupported() {
    278         return false;
    279     }
    280 
    281     /**
    282      * <i>This operation is not supported</i>.
    283      *
    284      * @param limit maximum bytes that can be read before invalidating the position marker
    285      */
    286     public void mark(int limit) {
    287         // Operation not supported
    288     }
    289 
    290     /**
    291      * <i>This operation is not supported</i>.
    292      *
    293      * @throws IOException always thrown
    294      */
    295     public void reset() throws IOException {
    296         throw new IOException("mark/reset not supported");
    297     }
    298 }
    299