1   /*
2    * %W% %E%
3    *
4    * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
5    * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6    */
7   
8   package java.util.zip;
9   
10  /**
11   * This class provides support for general purpose compression using the
12   * popular ZLIB compression library. The ZLIB compression library was
13   * initially developed as part of the PNG graphics standard and is not
14   * protected by patents. It is fully described in the specifications at 
15   * the <a href="package-summary.html#package_description">java.util.zip
16   * package description</a>.
17   *
18   * <p>The following code fragment demonstrates a trivial compression
19   * and decompression of a string using <tt>Deflater</tt> and
20   * <tt>Inflater</tt>.
21   *
22   * <blockquote><pre>
23   * try {
24   *     // Encode a String into bytes
25   *     String inputString = "blahblahblah€€";
26   *     byte[] input = inputString.getBytes("UTF-8");
27   *
28   *     // Compress the bytes
29   *     byte[] output = new byte[100];
30   *     Deflater compresser = new Deflater();
31   *     compresser.setInput(input);
32   *     compresser.finish();
33   *     int compressedDataLength = compresser.deflate(output);
34   *
35   *     // Decompress the bytes
36   *     Inflater decompresser = new Inflater();
37   *     decompresser.setInput(output, 0, compressedDataLength);
38   *     byte[] result = new byte[100];
39   *     int resultLength = decompresser.inflate(result);
40   *     decompresser.end();
41   *
42   *     // Decode the bytes into a String
43   *     String outputString = new String(result, 0, resultLength, "UTF-8");
44   * } catch(java.io.UnsupportedEncodingException ex) {
45   *     // handle
46   * } catch (java.util.zip.DataFormatException ex) {
47   *     // handle
48   * }
49   * </pre></blockquote>
50   *
51   * @see     Inflater
52   * @version     %I%, %G%
53   * @author  David Connelly
54   */
55  public
56  class Deflater {
57  
58      private final ZStreamRef zsRef;
59      private byte[] buf = new byte[0];
60      private int off, len;
61      private int level, strategy;
62      private boolean setParams;
63      private boolean finish, finished;
64  
65      /**
66       * Compression method for the deflate algorithm (the only one currently
67       * supported).
68       */
69      public static final int DEFLATED = 8;
70  
71      /**
72       * Compression level for no compression.
73       */
74      public static final int NO_COMPRESSION = 0;
75  
76      /**
77       * Compression level for fastest compression.
78       */
79      public static final int BEST_SPEED = 1;
80  
81      /**
82       * Compression level for best compression.
83       */
84      public static final int BEST_COMPRESSION = 9;
85  
86      /**
87       * Default compression level.
88       */
89      public static final int DEFAULT_COMPRESSION = -1;
90  
91      /**
92       * Compression strategy best used for data consisting mostly of small
93       * values with a somewhat random distribution. Forces more Huffman coding
94       * and less string matching.
95       */
96      public static final int FILTERED = 1;
97  
98      /**
99       * Compression strategy for Huffman coding only.
100      */
101     public static final int HUFFMAN_ONLY = 2;
102 
103     /**
104      * Default compression strategy.
105      */
106     public static final int DEFAULT_STRATEGY = 0;
107 
108     static {
109     /* Zip library is loaded from System.initializeSystemClass */
110     initIDs();
111     }
112 
113     /**
114      * Creates a new compressor using the specified compression level.
115      * If 'nowrap' is true then the ZLIB header and checksum fields will
116      * not be used in order to support the compression format used in
117      * both GZIP and PKZIP.
118      * @param level the compression level (0-9)
119      * @param nowrap if true then use GZIP compatible compression
120      */
121     public Deflater(int level, boolean nowrap) {
122     this.level = level;
123     this.strategy = DEFAULT_STRATEGY;
124         this.zsRef = new ZStreamRef(init(level, DEFAULT_STRATEGY, nowrap));
125     }
126 
127     /** 
128      * Creates a new compressor using the specified compression level.
129      * Compressed data will be generated in ZLIB format.
130      * @param level the compression level (0-9)
131      */
132     public Deflater(int level) {
133     this(level, false);
134     }
135 
136     /**
137      * Creates a new compressor with the default compression level.
138      * Compressed data will be generated in ZLIB format.
139      */
140     public Deflater() {
141     this(DEFAULT_COMPRESSION, false);
142     }
143 
144     /**
145      * Sets input data for compression. This should be called whenever
146      * needsInput() returns true indicating that more input data is required.
147      * @param b the input data bytes
148      * @param off the start offset of the data
149      * @param len the length of the data
150      * @see Deflater#needsInput
151      */
152     public void setInput(byte[] b, int off, int len) {
153         if (b== null) {
154             throw new NullPointerException();
155         }
156         if (off < 0 || len < 0 || off > b.length - len) {
157             throw new ArrayIndexOutOfBoundsException();
158         }
159         synchronized (zsRef) {
160             this.buf = b;
161             this.off = off;
162             this.len = len;
163         }
164     }
165 
166     /**
167      * Sets input data for compression. This should be called whenever
168      * needsInput() returns true indicating that more input data is required.
169      * @param b the input data bytes
170      * @see Deflater#needsInput
171      */
172     public void setInput(byte[] b) {
173     setInput(b, 0, b.length);
174     }
175 
176     /**
177      * Sets preset dictionary for compression. A preset dictionary is used
178      * when the history buffer can be predetermined. When the data is later
179      * uncompressed with Inflater.inflate(), Inflater.getAdler() can be called
180      * in order to get the Adler-32 value of the dictionary required for
181      * decompression.
182      * @param b the dictionary data bytes
183      * @param off the start offset of the data
184      * @param len the length of the data
185      * @see Inflater#inflate
186      * @see Inflater#getAdler
187      */
188     public void setDictionary(byte[] b, int off, int len) {
189         if (b == null) {
190             throw new NullPointerException();
191         }
192         if (off < 0 || len < 0 || off > b.length - len) {
193             throw new ArrayIndexOutOfBoundsException();
194         }
195         synchronized (zsRef) {
196             ensureOpen();
197             setDictionary(zsRef.address(), b, off, len);
198         }
199     }
200 
201     /**
202      * Sets preset dictionary for compression. A preset dictionary is used
203      * when the history buffer can be predetermined. When the data is later
204      * uncompressed with Inflater.inflate(), Inflater.getAdler() can be called
205      * in order to get the Adler-32 value of the dictionary required for
206      * decompression.
207      * @param b the dictionary data bytes
208      * @see Inflater#inflate
209      * @see Inflater#getAdler
210      */
211     public void setDictionary(byte[] b) {
212     setDictionary(b, 0, b.length);
213     }
214 
215     /**
216      * Sets the compression strategy to the specified value.
217      * @param strategy the new compression strategy
218      * @exception IllegalArgumentException if the compression strategy is
219      *                         invalid
220      */
221     public void setStrategy(int strategy) {
222     switch (strategy) {
223       case DEFAULT_STRATEGY:
224       case FILTERED:
225       case HUFFMAN_ONLY:
226         break;
227       default:
228         throw new IllegalArgumentException();
229     }
230         synchronized (zsRef) {
231             if (this.strategy != strategy) {
232                 this.strategy = strategy;
233                 setParams = true;
234             }
235         }
236     }
237 
238     /**
239      * Sets the current compression level to the specified value.
240      * @param level the new compression level (0-9)
241      * @exception IllegalArgumentException if the compression level is invalid
242      */
243     public void setLevel(int level) {
244     if ((level < 0 || level > 9) && level != DEFAULT_COMPRESSION) {
245         throw new IllegalArgumentException("invalid compression level");
246     }
247         synchronized (zsRef) {
248             if (this.level != level) {
249                 this.level = level;
250                 setParams = true;
251             }
252         }
253     }
254 
255     /**
256      * Returns true if the input data buffer is empty and setInput()
257      * should be called in order to provide more input.
258      * @return true if the input data buffer is empty and setInput()
259      * should be called in order to provide more input
260      */
261     public boolean needsInput() {
262     return len <= 0;
263     }
264 
265     /**
266      * When called, indicates that compression should end with the current
267      * contents of the input buffer.
268      */
269     public void finish() {
270         synchronized (zsRef) {
271             finish = true;
272         }
273     }
274 
275     /**
276      * Returns true if the end of the compressed data output stream has
277      * been reached.
278      * @return true if the end of the compressed data output stream has
279      * been reached
280      */
281     public boolean finished() {
282         synchronized (zsRef) {
283             return finished;
284         }
285     }
286 
287     /**
288      * Fills specified buffer with compressed data. Returns actual number
289      * of bytes of compressed data. A return value of 0 indicates that
290      * needsInput() should be called in order to determine if more input
291      * data is required.
292      * @param b the buffer for the compressed data
293      * @param off the start offset of the data
294      * @param len the maximum number of bytes of compressed data
295      * @return the actual number of bytes of compressed data
296      */
297     public int deflate(byte[] b, int off, int len) {
298     if (b == null) {
299         throw new NullPointerException();
300     }
301         if (off < 0 || len < 0 || off > b.length - len) {
302         throw new ArrayIndexOutOfBoundsException();
303     }
304         synchronized (zsRef) {
305             ensureOpen();
306         return deflateBytes(zsRef.address(), b, off, len);
307         }
308     }
309 
310     /**
311      * Fills specified buffer with compressed data. Returns actual number
312      * of bytes of compressed data. A return value of 0 indicates that
313      * needsInput() should be called in order to determine if more input
314      * data is required.
315      * @param b the buffer for the compressed data
316      * @return the actual number of bytes of compressed data
317      */
318     public int deflate(byte[] b) {
319     return deflate(b, 0, b.length);
320     }
321 
322     /**
323      * Returns the ADLER-32 value of the uncompressed data.
324      * @return the ADLER-32 value of the uncompressed data
325      */
326     public int getAdler() {
327         synchronized (zsRef) {
328             ensureOpen();
329             return getAdler(zsRef.address());
330         }
331     }
332 
333     /**
334      * Returns the total number of uncompressed bytes input so far.
335      *
336      * <p>Since the number of bytes may be greater than
337      * Integer.MAX_VALUE, the {@link #getBytesRead()} method is now
338      * the preferred means of obtaining this information.</p>
339      *
340      * @return the total number of uncompressed bytes input so far
341      */
342     public int getTotalIn() {
343     return (int) getBytesRead();
344     }
345 
346     /**
347      * Returns the total number of uncompressed bytes input so far.</p>
348      *
349      * @return the total (non-negative) number of uncompressed bytes input so far
350      * @since 1.5
351      */
352     public long getBytesRead() {
353         synchronized (zsRef) {
354             ensureOpen();
355             return getBytesRead(zsRef.address());
356         }
357     }
358 
359     /**
360      * Returns the total number of compressed bytes output so far.
361      *
362      * <p>Since the number of bytes may be greater than
363      * Integer.MAX_VALUE, the {@link #getBytesWritten()} method is now
364      * the preferred means of obtaining this information.</p>
365      *
366      * @return the total number of compressed bytes output so far
367      */
368     public int getTotalOut() {
369     return (int) getBytesWritten();
370     }
371 
372     /**
373      * Returns the total number of compressed bytes output so far.</p>
374      *
375      * @return the total (non-negative) number of compressed bytes output so far
376      * @since 1.5
377      */
378     public long getBytesWritten() {
379         synchronized (zsRef) {
380             ensureOpen();
381             return getBytesWritten(zsRef.address());
382         }
383     }
384 
385     /**
386      * Resets deflater so that a new set of input data can be processed.
387      * Keeps current compression level and strategy settings.
388      */
389     public void reset() {
390         synchronized (zsRef) {
391             ensureOpen();
392             reset(zsRef.address());
393             finish = false;
394             finished = false;
395             off = len = 0;
396         }
397     }
398 
399     /**
400      * Closes the compressor and discards any unprocessed input.
401      * This method should be called when the compressor is no longer
402      * being used, but will also be called automatically by the
403      * finalize() method. Once this method is called, the behavior
404      * of the Deflater object is undefined.
405      */
406     public void end() {
407         synchronized (zsRef) {
408             long addr = zsRef.address();
409             zsRef.clear();
410             if (addr != 0) {
411                 end(addr);
412                 buf = null;
413             }
414         }
415     }
416 
417     /**
418      * Closes the compressor when garbage is collected.
419      */
420     protected void finalize() {
421     end();
422     }
423 
424     private void ensureOpen() {
425         assert Thread.holdsLock(zsRef);
426     if (zsRef.address() == 0)
427             throw new NullPointerException("Deflater has been closed");
428     }
429 
430     private static native void initIDs();
431     private native static long init(int level, int strategy, boolean nowrap);
432     private native static void setDictionary(long addr, byte[] b, int off,
433                          int len);
434     private native int deflateBytes(long addr, byte[] b, int off, int len);
435     private native static int getAdler(long addr);
436     private native static long getBytesRead(long addr);
437     private native static long getBytesWritten(long addr);
438     private native static void reset(long addr);
439     private native static void end(long addr);
440 }
441