Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: edu/emory/mathcs/util/io/CompressedOutputStream.java


1   /* ***** BEGIN LICENSE BLOCK *****
2    * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3    *
4    * The contents of this file are subject to the Mozilla Public License Version
5    * 1.1 (the "License"); you may not use this file except in compliance with
6    * the License. You may obtain a copy of the License at
7    * http://www.mozilla.org/MPL/
8    *
9    * Software distributed under the License is distributed on an "AS IS" basis,
10   * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11   * for the specific language governing rights and limitations under the
12   * License.
13   *
14   * The Original Code is the Emory Utilities.
15   *
16   * The Initial Developer of the Original Code is
17   * The Distributed Computing Laboratory, Emory University.
18   * Portions created by the Initial Developer are Copyright (C) 2002
19   * the Initial Developer. All Rights Reserved.
20   *
21   * Alternatively, the contents of this file may be used under the terms of
22   * either the GNU General Public License Version 2 or later (the "GPL"), or
23   * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
24   * in which case the provisions of the GPL or the LGPL are applicable instead
25   * of those above. If you wish to allow use of your version of this file only
26   * under the terms of either the GPL or the LGPL, and not to allow others to
27   * use your version of this file under the terms of the MPL, indicate your
28   * decision by deleting the provisions above and replace them with the notice
29   * and other provisions required by the GPL or the LGPL. If you do not delete
30   * the provisions above, a recipient may use your version of this file under
31   * the terms of any one of the MPL, the GPL or the LGPL.
32   *
33   * ***** END LICENSE BLOCK ***** */
34  
35  package edu.emory.mathcs.util.io;
36  
37  import java.io.*;
38  import java.util.zip.*;
39  
40  /**
41   * Filter output stream that compresses data and features strong
42   * flush semantics. Data is written as gzipped packets of variable size.
43   * Flushing causes immediate ending of currently written packet and sending all
44   * the data off. Therefore, this stream can be used as a transport for RMI or
45   * RPC.
46   *
47   * Note that standard {@link java.util.zip.ZipOutputStream} and
48   * {@link java.util.zip.GZipOutputStream} are useless for this purpose due to
49   * their insufficiently strong flushing semantics: they don't guarantee that
50   * flush sends out all the data that was written so far, which leads to
51   * deadlocks in request-response-based protocols.
52   * <p>
53   * Compression ratio decreases with decreasing packet size.
54   * Hence, the gain will be low or none in RMI applications that exchange
55   * small chunks of data. Since the protocol adds two bytes of metadata per each
56   * packet, in the extreme case (flushing every single byte) the number of
57   * bytes actually sent is tripled. On the other hand, the compression ratio
58   * may be significant if large chunks are exchanged, e.g. if large
59   * arrays are sent as parameters or received as return values.
60   *
61   * @see CompressedInputStream
62   *
63   * @author Dawid Kurzyniec
64   * @version 1.0
65   */
66  public class CompressedOutputStream extends FilterOutputStream {
67  
68      final static short DEFLATED = (short)0x8000;
69      final static short STORED   = (short)0x0000;
70      final static int   STORE_TRESHOLD = 32;
71  
72      final byte[] buf;
73      final byte[] destbuf;
74      final DataOutputStream dos;
75      int pos;
76      final Deflater deflater;
77  
78      public CompressedOutputStream(OutputStream out) {
79          this(out, 8192);
80      }
81  
82      public CompressedOutputStream(OutputStream out, int bufSize) {
83          super(out);
84          if (bufSize <= 0x0010 || bufSize > 0x7FFF) {
85              throw new IllegalArgumentException("Invalid buffer size");
86          }
87          this.buf = new byte[bufSize];
88          this.destbuf = new byte[bufSize];
89          this.deflater = new Deflater(Deflater.DEFAULT_COMPRESSION, true);
90          this.pos = 0;
91          this.dos = new DataOutputStream(out);
92      }
93  
94      public synchronized void write(int b) throws IOException {
95          buf[pos++] = (byte)b;
96          if (pos == buf.length) {
97              writePacket();
98          }
99      }
100 
101     public synchronized void write(byte[] data, int off, int len) throws IOException {
102         while (len > 0) {
103             int todo = Math.min(buf.length-pos, len);
104             System.arraycopy(data, off, buf, pos, todo);
105             pos += todo;
106             off += todo;
107             len -= todo;
108             if (pos == buf.length) {
109                 writePacket();
110             }
111         }
112     }
113 
114     public synchronized void flush() throws IOException {
115         // make sure we don't write out empty packets
116         if (pos != 0) writePacket();
117         out.flush();
118     }
119 
120     private synchronized void writePacket() throws IOException {
121         short header;
122         if (pos < STORE_TRESHOLD) {
123             // store only
124             header = (short)((short)pos | STORED);
125             dos.writeShort(header);
126             out.write(buf, 0, pos);
127         }
128         else {
129             // check whichever gets smaller packet
130             deflater.reset();
131             deflater.setInput(buf, 0, pos);
132             deflater.finish();
133             int off = 0;
134             while (!deflater.finished() && off < destbuf.length) {
135                 int wrote = deflater.deflate(destbuf, off, destbuf.length - off);
136                 assert (wrote > 0);
137                 off += wrote;
138             }
139             if (off < destbuf.length - 2) {
140                 // deflate
141                 header = (short)((short)off | DEFLATED);
142                 dos.writeShort(header);
143                 out.write(destbuf, 0, off);
144             }
145             else {
146                 // store only
147                 header = (short)((short)pos | STORED);
148                 dos.writeShort(header);
149                 out.write(buf, 0, pos);
150             }
151         }
152         pos = 0;
153     }
154 }