File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\tools\CompressTool.java.html

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 */
7 package org.h2.tools;
8
9 import java.io.IOException;
10 import java.io.InputStream;
11 import java.io.OutputStream;
12 import java.util.zip.DeflaterOutputStream;
13 import java.util.zip.GZIPInputStream;
14 import java.util.zip.GZIPOutputStream;
15 import java.util.zip.InflaterInputStream;
16 import java.util.zip.ZipEntry;
17 import java.util.zip.ZipInputStream;
18 import java.util.zip.ZipOutputStream;
19
20 import org.h2.compress.CompressDeflate;
21 import org.h2.compress.CompressLZF;
22 import org.h2.compress.CompressNo;
23 import org.h2.compress.Compressor;
24 import org.h2.compress.LZFInputStream;
25 import org.h2.compress.LZFOutputStream;
26 import org.h2.constant.ErrorCode;
27 import org.h2.engine.Constants;
28 import org.h2.message.DbException;
29 import org.h2.util.Utils;
30 import org.h2.util.StringUtils;
31
32 /**
33 * A tool to losslessly compress data, and expand the compressed data again.
34 */
35 public class CompressTool {
36
37 private static final int MAX_BUFFER_SIZE = 3 * Constants.IO_BUFFER_SIZE_COMPRESS;
38 private byte[] cachedBuffer;
39
40 private CompressTool() {
41 // don't allow construction
42 }
43
44 private byte[] getBuffer(int min) {
45 if (min > MAX_BUFFER_SIZE) {
46 return Utils.newBytes(min);
47 }
48 if (cachedBuffer == null || cachedBuffer.length < min) {
49 cachedBuffer = Utils.newBytes(min);
50 }
51 return cachedBuffer;
52 }
53
54 /**
55 * Get a new instance. Each instance uses a separate buffer, so multiple
56 * instances can be used concurrently. However each instance alone is not
57 * multithreading safe.
58 *
59 * @return a new instance
60 */
61 public static CompressTool getInstance() {
62 return new CompressTool();
63 }
64
65 /**
66 * Compressed the data using the specified algorithm. If no algorithm is
67 * supplied, LZF is used
68 *
69 * @param in the byte array with the original data
70 * @param algorithm the algorithm (LZF, DEFLATE)
71 * @return the compressed data
72 */
73 public byte[] compress(byte[] in, String algorithm) {
74 int len = in.length;
75 if (in.length < 5) {
76 algorithm = "NO";
77 }
78 Compressor compress = getCompressor(algorithm);
79 byte[] buff = getBuffer((len < 100 ? len + 100 : len) * 2);
80 int newLen = compress(in, in.length, compress, buff);
81 byte[] out = Utils.newBytes(newLen);
82 System.arraycopy(buff, 0, out, 0, newLen);
83 return out;
84 }
85
86 private static int compress(byte[] in, int len, Compressor compress, byte[] out) {
87 int newLen = 0;
88 out[0] = (byte) compress.getAlgorithm();
89 int start = 1 + writeVariableInt(out, 1, len);
90 newLen = compress.compress(in, len, out, start);
91 if (newLen > len + start || newLen <= 0) {
92 out[0] = Compressor.NO;
93 System.arraycopy(in, 0, out, start, len);
94 newLen = len + start;
95 }
96 return newLen;
97 }
98
99 /**
100 * Expands the compressed data.
101 *
102 * @param in the byte array with the compressed data
103 * @return the uncompressed data
104 */
105 public byte[] expand(byte[] in) {
106 int algorithm = in[0];
107 Compressor compress = getCompressor(algorithm);
108 try {
109 int len = readVariableInt(in, 1);
110 int start = 1 + getVariableIntLength(len);
111 byte[] buff = Utils.newBytes(len);
112 compress.expand(in, start, in.length - start, buff, 0, len);
113 return buff;
114 } catch (Exception e) {
115 throw DbException.get(ErrorCode.COMPRESSION_ERROR, e);
116 }
117 }
118
119 /**
120 * INTERNAL
121 */
122 public static void expand(byte[] in, byte[] out, int outPos) {
123 int algorithm = in[0];
124 Compressor compress = getCompressor(algorithm);
125 try {
126 int len = readVariableInt(in, 1);
127 int start = 1 + getVariableIntLength(len);
128 compress.expand(in, start, in.length - start, out, outPos, len);
129 } catch (Exception e) {
130 throw DbException.get(ErrorCode.COMPRESSION_ERROR, e);
131 }
132 }
133
134 /**
135 * Read a variable size integer using Rice coding.
136 *
137 * @param buff the buffer
138 * @param pos the position
139 * @return the integer
140 */
141 public static int readVariableInt(byte[] buff, int pos) {
142 int x = buff[pos++] & 0xff;
143 if (x < 0x80) {
144 return x;
145 }
146 if (x < 0xc0) {
147 return ((x & 0x3f) << 8) + (buff[pos] & 0xff);
148 }
149 if (x < 0xe0) {
150 return ((x & 0x1f) << 16) + ((buff[pos++] & 0xff) << 8) + (buff[pos] & 0xff);
151 }
152 if (x < 0xf0) {
153 return ((x & 0xf) << 24) + ((buff[pos++] & 0xff) << 16) + ((buff[pos++] & 0xff) << 8)
154 + (buff[pos] & 0xff);
155 }
156 return ((buff[pos++] & 0xff) << 24) + ((buff[pos++] & 0xff) << 16) + ((buff[pos++] & 0xff) << 8)
157 + (buff[pos] & 0xff);
158 }
159
160 /**
161 * Write a variable size integer using Rice coding.
162 * Negative values need 5 bytes.
163 *
164 * @param buff the buffer
165 * @param pos the position
166 * @param x the value
167 * @return the number of bytes written (0-5)
168 */
169 public static int writeVariableInt(byte[] buff, int pos, int x) {
170 if (x < 0) {
171 buff[pos++] = (byte) 0xf0;
172 buff[pos++] = (byte) (x >> 24);
173 buff[pos++] = (byte) (x >> 16);
174 buff[pos++] = (byte) (x >> 8);
175 buff[pos] = (byte) x;
176 return 5;
177 } else if (x < 0x80) {
178 buff[pos] = (byte) x;
179 return 1;
180 } else if (x < 0x4000) {
181 buff[pos++] = (byte) (0x80 | (x >> 8));
182 buff[pos] = (byte) x;
183 return 2;
184 } else if (x < 0x200000) {
185 buff[pos++] = (byte) (0xc0 | (x >> 16));
186 buff[pos++] = (byte) (x >> 8);
187 buff[pos] = (byte) x;
188 return 3;
189 } else if (x < 0x10000000) {
190 buff[pos++] = (byte) (0xe0 | (x >> 24));
191 buff[pos++] = (byte) (x >> 16);
192 buff[pos++] = (byte) (x >> 8);
193 buff[pos] = (byte) x;
194 return 4;
195 } else {
196 buff[pos++] = (byte) 0xf0;
197 buff[pos++] = (byte) (x >> 24);
198 buff[pos++] = (byte) (x >> 16);
199 buff[pos++] = (byte) (x >> 8);
200 buff[pos] = (byte) x;
201 return 5;
202 }
203 }
204
205 /**
206 * Get a variable size integer length using Rice coding.
207 * Negative values need 5 bytes.
208 *
209 * @param x the value
210 * @return the number of bytes needed (0-5)
211 */
212 public static int getVariableIntLength(int x) {
213 if (x < 0) {
214 return 5;
215 } else if (x < 0x80) {
216 return 1;
217 } else if (x < 0x4000) {
218 return 2;
219 } else if (x < 0x200000) {
220 return 3;
221 } else if (x < 0x10000000) {
222 return 4;
223 } else {
224 return 5;
225 }
226 }
227
228 private static Compressor getCompressor(String algorithm) {
229 if (algorithm == null) {
230 algorithm = "LZF";
231 }
232 int idx = algorithm.indexOf(' ');
233 String options = null;
234 if (idx > 0) {
235 options = algorithm.substring(idx + 1);
236 algorithm = algorithm.substring(0, idx);
237 }
238 int a = getCompressAlgorithm(algorithm);
239 Compressor compress = getCompressor(a);
240 compress.setOptions(options);
241 return compress;
242 }
243
244 /**
245 * INTERNAL
246 */
247 public static int getCompressAlgorithm(String algorithm) {
248 algorithm = StringUtils.toUpperEnglish(algorithm);
249 if ("NO".equals(algorithm)) {
250 return Compressor.NO;
251 } else if ("LZF".equals(algorithm)) {
252 return Compressor.LZF;
253 } else if ("DEFLATE".equals(algorithm)) {
254 return Compressor.DEFLATE;
255 } else {
256 throw DbException.get(ErrorCode.UNSUPPORTED_COMPRESSION_ALGORITHM_1, algorithm);
257 }
258 }
259
260 private static Compressor getCompressor(int algorithm) {
261 switch (algorithm) {
262 case Compressor.NO:
263 return new CompressNo();
264 case Compressor.LZF:
265 return new CompressLZF();
266 case Compressor.DEFLATE:
267 return new CompressDeflate();
268 default:
269 throw DbException.get(ErrorCode.UNSUPPORTED_COMPRESSION_ALGORITHM_1, "" + algorithm);
270 }
271 }
272
273 /**
274 * INTERNAL
275 */
276 public static OutputStream wrapOutputStream(OutputStream out, String compressionAlgorithm, String entryName) {
277 try {
278 if ("GZIP".equals(compressionAlgorithm)) {
279 out = new GZIPOutputStream(out);
280 } else if ("ZIP".equals(compressionAlgorithm)) {
281 ZipOutputStream z = new ZipOutputStream(out);
282 z.putNextEntry(new ZipEntry(entryName));
283 out = z;
284 } else if ("DEFLATE".equals(compressionAlgorithm)) {
285 out = new DeflaterOutputStream(out);
286 } else if ("LZF".equals(compressionAlgorithm)) {
287 out = new LZFOutputStream(out);
288 } else if (compressionAlgorithm != null) {
289 throw DbException.get(ErrorCode.UNSUPPORTED_COMPRESSION_ALGORITHM_1, compressionAlgorithm);
290 }
291 return out;
292 } catch (IOException e) {
293 throw DbException.convertIOException(e, null);
294 }
295 }
296
297 /**
298 * INTERNAL
299 */
300 public static InputStream wrapInputStream(InputStream in, String compressionAlgorithm, String entryName) {
301 try {
302 if ("GZIP".equals(compressionAlgorithm)) {
303 in = new GZIPInputStream(in);
304 } else if ("ZIP".equals(compressionAlgorithm)) {
305 ZipInputStream z = new ZipInputStream(in);
306 while (true) {
307 ZipEntry entry = z.getNextEntry();
308 if (entry == null) {
309 return null;
310 }
311 if (entryName.equals(entry.getName())) {
312 break;
313 }
314 }
315 in = z;
316 } else if ("DEFLATE".equals(compressionAlgorithm)) {
317 in = new InflaterInputStream(in);
318 } else if ("LZF".equals(compressionAlgorithm)) {
319 in = new LZFInputStream(in);
320 } else if (compressionAlgorithm != null) {
321 throw DbException.get(ErrorCode.UNSUPPORTED_COMPRESSION_ALGORITHM_1, compressionAlgorithm);
322 }
323 return in;
324 } catch (IOException e) {
325 throw DbException.convertIOException(e, null);
326 }
327 }
328
329 }
330
331