Home | History | Annotate | Download | only in zip
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one or more
      3  * contributor license agreements.  See the NOTICE file distributed with
      4  * this work for additional information regarding copyright ownership.
      5  * The ASF licenses this file to You under the Apache License, Version 2.0
      6  * (the "License"); you may not use this file except in compliance with
      7  * the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package java.util.zip;
     19 
     20 import java.io.FilterOutputStream;
     21 import java.io.IOException;
     22 import java.io.OutputStream;
     23 import java.util.Arrays;
     24 import libcore.io.Streams;
     25 
     26 /**
     27  * This class provides an implementation of {@code FilterOutputStream} that
     28  * compresses data using the <i>DEFLATE</i> algorithm. Basically it wraps the
     29  * {@code Deflater} class and takes care of the buffering.
     30  *
     31  * @see Deflater
     32  */
     33 public class DeflaterOutputStream extends FilterOutputStream {
     34     static final int BUF_SIZE = 512;
     35 
     36     /**
     37      * The buffer for the data to be written to.
     38      */
     39     protected byte[] buf;
     40 
     41     /**
     42      * The deflater used.
     43      */
     44     protected Deflater def;
     45 
     46     boolean done = false;
     47 
     48     private final boolean syncFlush;
     49 
     50     /**
     51      * This constructor lets you pass the {@code Deflater} specifying the
     52      * compression algorithm.
     53      *
     54      * @param os
     55      *            is the {@code OutputStream} where to write the compressed data
     56      *            to.
     57      * @param def
     58      *            is the specific {@code Deflater} that is used to compress
     59      *            data.
     60      */
     61     public DeflaterOutputStream(OutputStream os, Deflater def) {
     62         this(os, def, BUF_SIZE, false);
     63     }
     64 
     65     /**
     66      * This is the most basic constructor. You only need to pass the {@code
     67      * OutputStream} to which the compressed data shall be written to. The
     68      * default settings for the {@code Deflater} and internal buffer are used.
     69      * In particular the {@code Deflater} produces a ZLIB header in the output
     70      * stream.
     71      *
     72      * @param os
     73      *            is the OutputStream where to write the compressed data to.
     74      */
     75     public DeflaterOutputStream(OutputStream os) {
     76         this(os, new Deflater(), BUF_SIZE, false);
     77     }
     78 
     79     /**
     80      * This constructor lets you specify both the compression algorithm as well
     81      * as the internal buffer size to be used.
     82      *
     83      * @param os
     84      *            is the {@code OutputStream} where to write the compressed data
     85      *            to.
     86      * @param def
     87      *            is the specific {@code Deflater} that will be used to compress
     88      *            data.
     89      * @param bufferSize
     90      *            is the size to be used for the internal buffer.
     91      */
     92     public DeflaterOutputStream(OutputStream os, Deflater def, int bufferSize) {
     93         this(os, def, bufferSize, false);
     94     }
     95 
     96     /**
     97      * @hide
     98      * @since 1.7
     99      */
    100     public DeflaterOutputStream(OutputStream os, boolean syncFlush) {
    101         this(os, new Deflater(), BUF_SIZE, syncFlush);
    102     }
    103 
    104     /**
    105      * @hide
    106      * @since 1.7
    107      */
    108     public DeflaterOutputStream(OutputStream os, Deflater def, boolean syncFlush) {
    109         this(os, def, BUF_SIZE, syncFlush);
    110     }
    111 
    112     /**
    113      * @hide
    114      * @since 1.7
    115      */
    116     public DeflaterOutputStream(OutputStream os, Deflater def, int bufferSize, boolean syncFlush) {
    117         super(os);
    118         if (os == null) {
    119             throw new NullPointerException("os == null");
    120         } else if (def == null) {
    121             throw new NullPointerException("def == null");
    122         }
    123         if (bufferSize <= 0) {
    124             throw new IllegalArgumentException("bufferSize <= 0: " + bufferSize);
    125         }
    126         this.def = def;
    127         this.syncFlush = syncFlush;
    128         buf = new byte[bufferSize];
    129     }
    130 
    131     /**
    132      * Compress the data in the input buffer and write it to the underlying
    133      * stream.
    134      *
    135      * @throws IOException
    136      *             If an error occurs during deflation.
    137      */
    138     protected void deflate() throws IOException {
    139         int byteCount;
    140         while ((byteCount = def.deflate(buf)) != 0) {
    141             out.write(buf, 0, byteCount);
    142         }
    143     }
    144 
    145     /**
    146      * Writes any unwritten compressed data to the underlying stream, the closes
    147      * all underlying streams. This stream can no longer be used after close()
    148      * has been called.
    149      *
    150      * @throws IOException
    151      *             If an error occurs while closing the data compression
    152      *             process.
    153      */
    154     @Override
    155     public void close() throws IOException {
    156         // everything closed here should also be closed in ZipOutputStream.close()
    157         if (!def.finished()) {
    158             finish();
    159         }
    160         def.end();
    161         out.close();
    162     }
    163 
    164     /**
    165      * Writes any unwritten data to the underlying stream. Does not close the
    166      * stream.
    167      *
    168      * @throws IOException
    169      *             If an error occurs.
    170      */
    171     public void finish() throws IOException {
    172         if (done) {
    173             return;
    174         }
    175         def.finish();
    176         while (!def.finished()) {
    177             int byteCount = def.deflate(buf);
    178             out.write(buf, 0, byteCount);
    179         }
    180         done = true;
    181     }
    182 
    183     @Override public void write(int i) throws IOException {
    184         Streams.writeSingleByte(this, i);
    185     }
    186 
    187     /**
    188      * Compresses {@code byteCount} bytes of data from {@code buf} starting at
    189      * {@code offset} and writes it to the underlying stream.
    190      * @throws IOException
    191      *             If an error occurs during writing.
    192      */
    193     @Override public void write(byte[] buffer, int offset, int byteCount) throws IOException {
    194         if (done) {
    195             throw new IOException("attempt to write after finish");
    196         }
    197         Arrays.checkOffsetAndCount(buffer.length, offset, byteCount);
    198         if (!def.needsInput()) {
    199             throw new IOException();
    200         }
    201         def.setInput(buffer, offset, byteCount);
    202         deflate();
    203     }
    204 
    205     /**
    206      * Flushes the underlying stream. This flushes only the bytes that can be
    207      * compressed at the highest level.
    208      *
    209      * <p>For deflater output streams constructed with Java 7's
    210      * {@code syncFlush} parameter set to true (not yet available on Android),
    211      * this first flushes all outstanding data so that it may be immediately
    212      * read by its recipient. Doing so may degrade compression.
    213      */
    214     @Override public void flush() throws IOException {
    215         if (syncFlush) {
    216             int byteCount;
    217             while ((byteCount = def.deflate(buf, 0, buf.length, Deflater.SYNC_FLUSH)) != 0) {
    218                 out.write(buf, 0, byteCount);
    219             }
    220         }
    221         out.flush();
    222     }
    223 }
    224