1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | */ |
6 | package org.h2.compress; |
7 | |
8 | import java.util.StringTokenizer; |
9 | import java.util.zip.DataFormatException; |
10 | import java.util.zip.Deflater; |
11 | import java.util.zip.Inflater; |
12 | |
13 | import org.h2.api.ErrorCode; |
14 | import org.h2.message.DbException; |
15 | |
16 | /** |
17 | * This is a wrapper class for the Deflater class. |
18 | * This algorithm supports the following options: |
19 | * <ul> |
20 | * <li>l or level: -1 (default), 0 (no compression), |
21 | * 1 (best speed), ..., 9 (best compression) |
22 | * </li><li>s or strategy: 0 (default), |
23 | * 1 (filtered), 2 (huffman only) |
24 | * </li></ul> |
25 | * See also java.util.zip.Deflater for details. |
26 | */ |
27 | public class CompressDeflate implements Compressor { |
28 | |
29 | private int level = Deflater.DEFAULT_COMPRESSION; |
30 | private int strategy = Deflater.DEFAULT_STRATEGY; |
31 | |
32 | @Override |
33 | public void setOptions(String options) { |
34 | if (options == null) { |
35 | return; |
36 | } |
37 | try { |
38 | StringTokenizer tokenizer = new StringTokenizer(options); |
39 | while (tokenizer.hasMoreElements()) { |
40 | String option = tokenizer.nextToken(); |
41 | if ("level".equals(option) || "l".equals(option)) { |
42 | level = Integer.parseInt(tokenizer.nextToken()); |
43 | } else if ("strategy".equals(option) || "s".equals(option)) { |
44 | strategy = Integer.parseInt(tokenizer.nextToken()); |
45 | } |
46 | Deflater deflater = new Deflater(level); |
47 | deflater.setStrategy(strategy); |
48 | } |
49 | } catch (Exception e) { |
50 | throw DbException.get(ErrorCode.UNSUPPORTED_COMPRESSION_OPTIONS_1, options); |
51 | } |
52 | } |
53 | |
54 | @Override |
55 | public int compress(byte[] in, int inLen, byte[] out, int outPos) { |
56 | Deflater deflater = new Deflater(level); |
57 | deflater.setStrategy(strategy); |
58 | deflater.setInput(in, 0, inLen); |
59 | deflater.finish(); |
60 | int compressed = deflater.deflate(out, outPos, out.length - outPos); |
61 | while (compressed == 0) { |
62 | // the compressed length is 0, meaning compression didn't work |
63 | // (sounds like a JDK bug) |
64 | // try again, using the default strategy and compression level |
65 | strategy = Deflater.DEFAULT_STRATEGY; |
66 | level = Deflater.DEFAULT_COMPRESSION; |
67 | return compress(in, inLen, out, outPos); |
68 | } |
69 | deflater.end(); |
70 | return outPos + compressed; |
71 | } |
72 | |
73 | @Override |
74 | public int getAlgorithm() { |
75 | return Compressor.DEFLATE; |
76 | } |
77 | |
78 | @Override |
79 | public void expand(byte[] in, int inPos, int inLen, byte[] out, int outPos, |
80 | int outLen) { |
81 | Inflater decompresser = new Inflater(); |
82 | decompresser.setInput(in, inPos, inLen); |
83 | decompresser.finished(); |
84 | try { |
85 | int len = decompresser.inflate(out, outPos, outLen); |
86 | if (len != outLen) { |
87 | throw new DataFormatException(len + " " + outLen); |
88 | } |
89 | } catch (DataFormatException e) { |
90 | throw DbException.get(ErrorCode.COMPRESSION_ERROR, e); |
91 | } |
92 | decompresser.end(); |
93 | } |
94 | |
95 | } |