1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4 
5 package IceInternal;
6 
7 public class BZip2
8 {
compress(Buffer buf, int headerSize, int compressionLevel)9     public static Buffer compress(Buffer buf, int headerSize, int compressionLevel)
10     {
11         assert(supported());
12 
13         int uncompressedLen = buf.size() - headerSize;
14         int compressedLen = (int)(uncompressedLen * 1.01 + 600);
15         byte[] compressed = new byte[compressedLen];
16 
17         byte[] data = null;
18         int offset = 0;
19         try
20         {
21             //
22             // If the ByteBuffer is backed by an array then we can avoid
23             // an extra copy by using the array directly.
24             //
25             data = buf.b.array();
26             offset = buf.b.arrayOffset();
27         }
28         catch(Exception ex)
29         {
30             //
31             // Otherwise, allocate an array to hold a copy of the uncompressed data.
32             //
33             data = new byte[buf.size()];
34             buf.position(0);
35             buf.b.get(data);
36         }
37 
38         try
39         {
40             //
41             // Compress the data using the class org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream.
42             // Its constructor requires an OutputStream argument, therefore we pass the
43             // compressed buffer in an OutputStream wrapper.
44             //
45             BufferedOutputStream bos = new BufferedOutputStream(compressed);
46             java.lang.Object[] args = new java.lang.Object[]{ bos, Integer.valueOf(compressionLevel) };
47             java.io.OutputStream os = (java.io.OutputStream)_bzOutputStreamCtor.newInstance(args);
48             os.write(data, offset + headerSize, uncompressedLen);
49             os.close();
50             compressedLen = bos.pos();
51         }
52         catch(Exception ex)
53         {
54             throw new Ice.CompressionException("bzip2 compression failure", ex);
55         }
56 
57         //
58         // Don't bother if the compressed data is larger than the
59         // uncompressed data.
60         //
61         if(compressedLen >= uncompressedLen)
62         {
63             return null;
64         }
65 
66         Buffer r = new Buffer(false);
67         r.resize(headerSize + 4 + compressedLen, false);
68         r.position(0);
69 
70         //
71         // Copy the header from the uncompressed stream to the compressed one.
72         //
73         r.b.put(data, offset, headerSize);
74 
75         //
76         // Add the size of the uncompressed stream before the message body.
77         //
78         r.b.putInt(buf.size());
79 
80         //
81         // Add the compressed message body.
82         //
83         r.b.put(compressed, 0, compressedLen);
84 
85         return r;
86     }
87 
uncompress(Buffer buf, int headerSize, int messageSizeMax)88     public static Buffer uncompress(Buffer buf, int headerSize, int messageSizeMax)
89     {
90         assert(supported());
91 
92         buf.position(headerSize);
93         int uncompressedSize = buf.b.getInt();
94         if(uncompressedSize <= headerSize)
95         {
96             throw new Ice.IllegalMessageSizeException();
97         }
98         if(uncompressedSize > messageSizeMax)
99         {
100             IceInternal.Ex.throwMemoryLimitException(uncompressedSize, messageSizeMax);
101         }
102 
103         int compressedLen = buf.size() - headerSize - 4;
104 
105         byte[] compressed = null;
106         int offset = 0;
107         try
108         {
109             //
110             // If the ByteBuffer is backed by an array then we can avoid
111             // an extra copy by using the array directly.
112             //
113             compressed = buf.b.array();
114             offset = buf.b.arrayOffset();
115         }
116         catch(Exception ex)
117         {
118             //
119             // Otherwise, allocate an array to hold a copy of the compressed data.
120             //
121             compressed = new byte[buf.size()];
122             buf.position(0);
123             buf.b.get(compressed);
124         }
125 
126         Buffer r = new Buffer(false);
127         r.resize(uncompressedSize, false);
128 
129         try
130         {
131             //
132             // Uncompress the data using the class org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream.
133             // Its constructor requires an InputStream argument, therefore we pass the
134             // compressed data in a ByteArrayInputStream.
135             //
136             java.io.ByteArrayInputStream bais =
137                 new java.io.ByteArrayInputStream(compressed, offset + headerSize + 4, compressedLen);
138             java.lang.Object[] args = new java.lang.Object[]{ bais };
139             java.io.InputStream is = (java.io.InputStream)_bzInputStreamCtor.newInstance(args);
140             r.position(headerSize);
141             byte[] arr = new byte[8 * 1024];
142             int n;
143             while((n = is.read(arr)) != -1)
144             {
145                 r.b.put(arr, 0, n);
146             }
147             is.close();
148         }
149         catch(Exception ex)
150         {
151             throw new Ice.CompressionException("bzip2 uncompression failure", ex);
152         }
153 
154         //
155         // Copy the header from the compressed stream to the uncompressed one.
156         //
157         r.position(0);
158         r.b.put(compressed, offset, headerSize);
159 
160         return r;
161     }
162 
163     private static boolean _checked = false;
164     private static java.lang.reflect.Constructor<?> _bzInputStreamCtor;
165     private static java.lang.reflect.Constructor<?> _bzOutputStreamCtor;
166 
supported()167     public synchronized static boolean supported()
168     {
169         //
170         // Use lazy initialization when determining whether support for bzip2 compression is available.
171         //
172         if(!_checked)
173         {
174             _checked = true;
175             try
176             {
177                 Class<?> cls;
178                 Class<?>[] types = new Class<?>[1];
179                 cls = IceInternal.Util.findClass("org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream", null);
180                 if(cls != null)
181                 {
182                     types[0] = java.io.InputStream.class;
183                     _bzInputStreamCtor = cls.getDeclaredConstructor(types);
184                 }
185                 cls = IceInternal.Util.findClass("org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream", null);
186                 if(cls != null)
187                 {
188                     types = new Class[2];
189                     types[0] = java.io.OutputStream.class;
190                     types[1] = Integer.TYPE;
191                     _bzOutputStreamCtor = cls.getDeclaredConstructor(types);
192                 }
193             }
194             catch(Exception ex)
195             {
196                 // Ignore - bzip2 compression not available.
197             }
198         }
199         return _bzInputStreamCtor != null && _bzOutputStreamCtor != null;
200     }
201 
202     private static class BufferedOutputStream extends java.io.OutputStream
203     {
BufferedOutputStream(byte[] data)204         BufferedOutputStream(byte[] data)
205         {
206             _data = data;
207         }
208 
209         @Override
close()210         public void close()
211             throws java.io.IOException
212         {
213         }
214 
215         @Override
flush()216         public void flush()
217             throws java.io.IOException
218         {
219         }
220 
221         @Override
write(byte[] b)222         public void write(byte[] b)
223             throws java.io.IOException
224         {
225             assert(_data.length - _pos >= b.length);
226             System.arraycopy(b, 0, _data, _pos, b.length);
227             _pos += b.length;
228         }
229 
230         @Override
write(byte[] b, int off, int len)231         public void write(byte[] b, int off, int len)
232             throws java.io.IOException
233         {
234             assert(_data.length - _pos >= len);
235             System.arraycopy(b, off, _data, _pos, len);
236             _pos += len;
237         }
238 
239         @Override
write(int b)240         public void write(int b)
241             throws java.io.IOException
242         {
243             assert(_data.length - _pos >= 1);
244             _data[_pos] = (byte)b;
245             ++_pos;
246         }
247 
248         int
pos()249         pos()
250         {
251             return _pos;
252         }
253 
254         private byte[] _data;
255         private int _pos;
256     }
257 }
258