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 dalvik.system.CloseGuard;
     21 import java.util.Arrays;
     22 import libcore.util.EmptyArray;
     23 
     24 /**
     25  * This class compresses data using the <i>DEFLATE</i> algorithm (see <a
     26  * href="http://www.gzip.org/algorithm.txt">specification</a>).
     27  *
     28  * <p>It is usually more convenient to use {@link DeflaterOutputStream}.
     29  *
     30  * <p>To compress an in-memory {@code byte[]} to another in-memory {@code byte[]} manually:
     31  * <pre>
     32  *     byte[] originalBytes = ...
     33  *
     34  *     Deflater deflater = new Deflater();
     35  *     deflater.setInput(originalBytes);
     36  *     deflater.finish();
     37  *
     38  *     ByteArrayOutputStream baos = new ByteArrayOutputStream();
     39  *     byte[] buf = new byte[8192];
     40  *     while (!deflater.finished()) {
     41  *         int byteCount = deflater.deflate(buf);
     42  *         baos.write(buf, 0, byteCount);
     43  *     }
     44  *     deflater.end();
     45  *
     46  *     byte[] compressedBytes = baos.toByteArray();
     47  * </pre>
     48  * <p>In situations where you don't have all the input in one array (or have so much
     49  * input that you want to feed it to the deflater in chunks), it's possible to call
     50  * {@link #setInput setInput} repeatedly, but you're much better off using
     51  * {@link DeflaterOutputStream} to handle all this for you. {@link DeflaterOutputStream} also helps
     52  * minimize memory requirements&nbsp;&mdash; the sample code above is very expensive.
     53  *
     54  * <a name="compression_level"><h3>Compression levels</h3></a>
     55  * <p>A compression level must be {@link #DEFAULT_COMPRESSION} to compromise between speed and
     56  * compression (currently equivalent to level 6), or between 0 ({@link #NO_COMPRESSION}, where
     57  * the input is simply copied) and 9 ({@link #BEST_COMPRESSION}). Level 1 ({@link #BEST_SPEED})
     58  * performs some compression, but with minimal speed overhead.
     59  */
     60 public class Deflater {
     61 
     62     /**
     63      * This <a href="#compression_level">compression level</a> gives the best compression,
     64      * but takes the most time.
     65      */
     66     public static final int BEST_COMPRESSION = 9;
     67 
     68     /**
     69      * This <a href="#compression_level">compression level</a> gives minimal compression,
     70      * but takes the least time (of any level that actually performs compression;
     71      * see {@link #NO_COMPRESSION}).
     72      */
     73     public static final int BEST_SPEED = 1;
     74 
     75     /**
     76      * This <a href="#compression_level">compression level</a> does no compression.
     77      * It is even faster than {@link #BEST_SPEED}.
     78      */
     79     public static final int NO_COMPRESSION = 0;
     80 
     81     /**
     82      * The default <a href="#compression_level">compression level</a>.
     83      * This is a trade-off between speed and compression, currently equivalent to level 6.
     84      */
     85     public static final int DEFAULT_COMPRESSION = -1;
     86 
     87     /**
     88      * The default compression strategy.
     89      */
     90     public static final int DEFAULT_STRATEGY = 0;
     91 
     92     /**
     93      * The default compression method.
     94      */
     95     public static final int DEFLATED = 8;
     96 
     97     /**
     98      * A compression strategy.
     99      */
    100     public static final int FILTERED = 1;
    101 
    102     /**
    103      * A compression strategy.
    104      */
    105     public static final int HUFFMAN_ONLY = 2;
    106 
    107     /**
    108      * Use buffering for best compression.
    109      * @since 1.7
    110      */
    111     public static final int NO_FLUSH = 0;
    112 
    113     /**
    114      * Flush buffers so recipients can immediately decode the data sent thus
    115      * far. This mode may degrade compression.
    116      * @since 1.7
    117      */
    118     public static final int SYNC_FLUSH = 2;
    119 
    120     /**
    121      * Flush buffers so recipients can immediately decode the data sent thus
    122      * far. The compression state is also reset to permit random access and
    123      * recovery for clients who have discarded or damaged their own copy. This
    124      * mode may degrade compression.
    125      * @since 1.7
    126      */
    127     public static final int FULL_FLUSH = 3;
    128 
    129     /**
    130      * Flush buffers and mark the end of the data stream.
    131      */
    132     private static final int FINISH = 4;
    133 
    134     /**
    135      * The ugly name flushParm is for RI compatibility, should code need to access this
    136      * field via reflection if it's not able to use public API to choose what
    137      * kind of flushing it gets.
    138      */
    139     private int flushParm = NO_FLUSH;
    140 
    141     private boolean finished;
    142 
    143     private int compressLevel = DEFAULT_COMPRESSION;
    144 
    145     private int strategy = DEFAULT_STRATEGY;
    146 
    147     private long streamHandle = -1;
    148 
    149     private byte[] inputBuffer;
    150 
    151     private int inRead;
    152 
    153     private int inLength;
    154 
    155     private final CloseGuard guard = CloseGuard.get();
    156 
    157     /**
    158      * Constructs a new {@code Deflater} instance using the
    159      * default <a href="#compression_level">compression level</a>.
    160      * The compression strategy can be specified with {@link #setStrategy}. A
    161      * header is added to the output by default; use {@link
    162      * #Deflater(int, boolean)} if you need to omit the header.
    163      */
    164     public Deflater() {
    165         this(DEFAULT_COMPRESSION, false);
    166     }
    167 
    168     /**
    169      * Constructs a new {@code Deflater} instance with the
    170      * given <a href="#compression_level">compression level</a>.
    171      * The compression strategy can be specified with {@link #setStrategy}.
    172      * A header is added to the output by default; use
    173      * {@link #Deflater(int, boolean)} if you need to omit the header.
    174      */
    175     public Deflater(int level) {
    176         this(level, false);
    177     }
    178 
    179     /**
    180      * Constructs a new {@code Deflater} instance with the
    181      * given <a href="#compression_level">compression level</a>.
    182      * If {@code noHeader} is true, no ZLIB header is added to the
    183      * output. In a zip file, every entry (compressed file) comes with such a
    184      * header. The strategy can be specified using {@link #setStrategy}.
    185      */
    186     public Deflater(int level, boolean noHeader) {
    187         if (level < DEFAULT_COMPRESSION || level > BEST_COMPRESSION) {
    188             throw new IllegalArgumentException("Bad level: " + level);
    189         }
    190         compressLevel = level;
    191         streamHandle = createStream(compressLevel, strategy, noHeader);
    192         guard.open("end");
    193     }
    194 
    195     /**
    196      * Deflates the data (previously passed to {@link #setInput setInput}) into the
    197      * supplied buffer.
    198      *
    199      * @return number of bytes of compressed data written to {@code buf}.
    200      */
    201     public int deflate(byte[] buf) {
    202         return deflate(buf, 0, buf.length);
    203     }
    204 
    205     /**
    206      * Deflates data (previously passed to {@link #setInput setInput}) into a specific
    207      * region within the supplied buffer.
    208      *
    209      * @return the number of bytes of compressed data written to {@code buf}.
    210      */
    211     public synchronized int deflate(byte[] buf, int offset, int byteCount) {
    212         return deflateImpl(buf, offset, byteCount, flushParm);
    213     }
    214 
    215     /**
    216      * Deflates data (previously passed to {@link #setInput setInput}) into a specific
    217      * region within the supplied buffer, optionally flushing the input buffer.
    218      *
    219      * @param flush one of {@link #NO_FLUSH}, {@link #SYNC_FLUSH} or {@link #FULL_FLUSH}.
    220      * @return the number of compressed bytes written to {@code buf}. If this
    221      *      equals {@code byteCount}, the number of bytes of input to be flushed
    222      *      may have exceeded the output buffer's capacity. In this case,
    223      *      finishing a flush will require the output buffer to be drained
    224      *      and additional calls to {@link #deflate} to be made.
    225      * @throws IllegalArgumentException if {@code flush} is invalid.
    226      * @since 1.7
    227      */
    228     public synchronized int deflate(byte[] buf, int offset, int byteCount, int flush) {
    229         if (flush != NO_FLUSH && flush != SYNC_FLUSH && flush != FULL_FLUSH) {
    230             throw new IllegalArgumentException("Bad flush value: " + flush);
    231         }
    232         return deflateImpl(buf, offset, byteCount, flush);
    233     }
    234 
    235     private synchronized int deflateImpl(byte[] buf, int offset, int byteCount, int flush) {
    236         checkOpen();
    237         Arrays.checkOffsetAndCount(buf.length, offset, byteCount);
    238         if (inputBuffer == null) {
    239             setInput(EmptyArray.BYTE);
    240         }
    241         return deflateImpl(buf, offset, byteCount, streamHandle, flush);
    242     }
    243 
    244     private native int deflateImpl(byte[] buf, int offset, int byteCount, long handle, int flushParm);
    245 
    246     /**
    247      * Frees all resources held onto by this deflating algorithm. Any unused
    248      * input or output is discarded. This method should be called explicitly in
    249      * order to free native resources as soon as possible. After {@code end()} is
    250      * called, other methods will typically throw {@code IllegalStateException}.
    251      */
    252     public synchronized void end() {
    253         guard.close();
    254         endImpl();
    255     }
    256 
    257     private void endImpl() {
    258         if (streamHandle != -1) {
    259             endImpl(streamHandle);
    260             inputBuffer = null;
    261             streamHandle = -1;
    262         }
    263     }
    264 
    265     private native void endImpl(long handle);
    266 
    267     @Override protected void finalize() {
    268         try {
    269             if (guard != null) {
    270                 guard.warnIfOpen();
    271             }
    272             synchronized (this) {
    273                 end(); // to allow overriding classes to clean up
    274                 endImpl(); // in case those classes don't call super.end()
    275             }
    276         } finally {
    277             try {
    278                 super.finalize();
    279             } catch (Throwable t) {
    280                 throw new AssertionError(t);
    281             }
    282         }
    283     }
    284 
    285     /**
    286      * Indicates to the {@code Deflater} that all uncompressed input has been provided
    287      * to it.
    288      *
    289      * @see #finished
    290      */
    291     public synchronized void finish() {
    292         flushParm = FINISH;
    293     }
    294 
    295     /**
    296      * Returns true if {@link #finish finish} has been called and all
    297      * data provided by {@link #setInput setInput} has been
    298      * successfully compressed and consumed by {@link #deflate deflate}.
    299      */
    300     public synchronized boolean finished() {
    301         return finished;
    302     }
    303 
    304     /**
    305      * Returns the {@link Adler32} checksum of the uncompressed data read so far.
    306      */
    307     public synchronized int getAdler() {
    308         checkOpen();
    309         return getAdlerImpl(streamHandle);
    310     }
    311 
    312     private native int getAdlerImpl(long handle);
    313 
    314     /**
    315      * Returns the total number of bytes of input read by this {@code Deflater}. This
    316      * method is limited to 32 bits; use {@link #getBytesRead} instead.
    317      */
    318     public synchronized int getTotalIn() {
    319         checkOpen();
    320         return (int) getTotalInImpl(streamHandle);
    321     }
    322 
    323     private native long getTotalInImpl(long handle);
    324 
    325     /**
    326      * Returns the total number of bytes written to the output buffer by this {@code
    327      * Deflater}. The method is limited to 32 bits; use {@link #getBytesWritten} instead.
    328      */
    329     public synchronized int getTotalOut() {
    330         checkOpen();
    331         return (int) getTotalOutImpl(streamHandle);
    332     }
    333 
    334     private native long getTotalOutImpl(long handle);
    335 
    336     /**
    337      * Returns true if {@link #setInput setInput} must be called before deflation can continue.
    338      * If all uncompressed data has been provided to the {@code Deflater},
    339      * {@link #finish} must be called to ensure the compressed data is output.
    340      */
    341     public synchronized boolean needsInput() {
    342         if (inputBuffer == null) {
    343             return true;
    344         }
    345         return inRead == inLength;
    346     }
    347 
    348     /**
    349      * Resets the {@code Deflater} to accept new input without affecting any
    350      * previously made settings for the compression strategy or level. This
    351      * operation <i>must</i> be called after {@link #finished} returns
    352      * true if the {@code Deflater} is to be reused.
    353      */
    354     public synchronized void reset() {
    355         checkOpen();
    356         flushParm = NO_FLUSH;
    357         finished = false;
    358         resetImpl(streamHandle);
    359         inputBuffer = null;
    360     }
    361 
    362     private native void resetImpl(long handle);
    363 
    364     /**
    365      * Sets the dictionary to be used for compression by this {@code Deflater}.
    366      * This method can only be called if this {@code Deflater} supports the writing
    367      * of ZLIB headers. This is the default, but can be overridden
    368      * using {@link #Deflater(int, boolean)}.
    369      */
    370     public void setDictionary(byte[] dictionary) {
    371         setDictionary(dictionary, 0, dictionary.length);
    372     }
    373 
    374     /**
    375      * Sets the dictionary to be used for compression by this {@code Deflater}.
    376      * This method can only be called if this {@code Deflater} supports the writing
    377      * of ZLIB headers. This is the default, but can be overridden
    378      * using {@link #Deflater(int, boolean)}.
    379      */
    380     public synchronized void setDictionary(byte[] buf, int offset, int byteCount) {
    381         checkOpen();
    382         Arrays.checkOffsetAndCount(buf.length, offset, byteCount);
    383         setDictionaryImpl(buf, offset, byteCount, streamHandle);
    384     }
    385 
    386     private native void setDictionaryImpl(byte[] buf, int offset, int byteCount, long handle);
    387 
    388     /**
    389      * Sets the input buffer the {@code Deflater} will use to extract uncompressed bytes
    390      * for later compression.
    391      */
    392     public void setInput(byte[] buf) {
    393         setInput(buf, 0, buf.length);
    394     }
    395 
    396     /**
    397      * Sets the input buffer the {@code Deflater} will use to extract uncompressed bytes
    398      * for later compression.
    399      */
    400     public synchronized void setInput(byte[] buf, int offset, int byteCount) {
    401         checkOpen();
    402         Arrays.checkOffsetAndCount(buf.length, offset, byteCount);
    403         inLength = byteCount;
    404         inRead = 0;
    405         if (inputBuffer == null) {
    406             setLevelsImpl(compressLevel, strategy, streamHandle);
    407         }
    408         inputBuffer = buf;
    409         setInputImpl(buf, offset, byteCount, streamHandle);
    410     }
    411 
    412     private native void setLevelsImpl(int level, int strategy, long handle);
    413 
    414     private native void setInputImpl(byte[] buf, int offset, int byteCount, long handle);
    415 
    416     /**
    417      * Sets the given <a href="#compression_level">compression level</a>
    418      * to be used when compressing data. This value must be set
    419      * prior to calling {@link #setInput setInput}.
    420      * @exception IllegalArgumentException
    421      *                If the compression level is invalid.
    422      */
    423     public synchronized void setLevel(int level) {
    424         if (level < DEFAULT_COMPRESSION || level > BEST_COMPRESSION) {
    425             throw new IllegalArgumentException("Bad level: " + level);
    426         }
    427         if (inputBuffer != null) {
    428             throw new IllegalStateException("setLevel cannot be called after setInput");
    429         }
    430         compressLevel = level;
    431     }
    432 
    433     /**
    434      * Sets the compression strategy to be used. The strategy must be one of
    435      * FILTERED, HUFFMAN_ONLY or DEFAULT_STRATEGY. This value must be set prior
    436      * to calling {@link #setInput setInput}.
    437      *
    438      * @exception IllegalArgumentException
    439      *                If the strategy specified is not one of FILTERED,
    440      *                HUFFMAN_ONLY or DEFAULT_STRATEGY.
    441      */
    442     public synchronized void setStrategy(int strategy) {
    443         if (strategy < DEFAULT_STRATEGY || strategy > HUFFMAN_ONLY) {
    444             throw new IllegalArgumentException("Bad strategy: " + strategy);
    445         }
    446         if (inputBuffer != null) {
    447             throw new IllegalStateException("setStrategy cannot be called after setInput");
    448         }
    449         this.strategy = strategy;
    450     }
    451 
    452     /**
    453      * Returns the total number of bytes read by the {@code Deflater}. This
    454      * method is the same as {@link #getTotalIn} except that it returns a
    455      * {@code long} value instead of an integer.
    456      */
    457     public synchronized long getBytesRead() {
    458         checkOpen();
    459         return getTotalInImpl(streamHandle);
    460     }
    461 
    462     /**
    463      * Returns a the total number of bytes written by this {@code Deflater}. This
    464      * method is the same as {@code getTotalOut} except it returns a
    465      * {@code long} value instead of an integer.
    466      */
    467     public synchronized long getBytesWritten() {
    468         checkOpen();
    469         return getTotalOutImpl(streamHandle);
    470     }
    471 
    472     private native long createStream(int level, int strategy1, boolean noHeader1);
    473 
    474     private void checkOpen() {
    475         if (streamHandle == -1) {
    476             throw new IllegalStateException("attempt to use Deflater after calling end");
    477         }
    478     }
    479 }
    480