EMMA Coverage Report (generated Sun Mar 01 22:06:14 CET 2015)
[all classes][org.h2.tools]

COVERAGE SUMMARY FOR SOURCE FILE [CompressTool.java]

nameclass, %method, %block, %line, %
CompressTool.java100% (1/1)100% (15/15)93%  (664/716)90%  (140/155)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class CompressTool100% (1/1)100% (15/15)93%  (664/716)90%  (140/155)
getCompressor (int): Compressor 100% (1/1)56%  (14/25)80%  (4/5)
expand (byte [], byte [], int): void 100% (1/1)81%  (29/36)78%  (7/9)
expand (byte []): byte [] 100% (1/1)82%  (32/39)78%  (7/9)
wrapInputStream (InputStream, String, String): InputStream 100% (1/1)83%  (57/69)75%  (15/20)
getCompressAlgorithm (String): int 100% (1/1)84%  (21/25)88%  (7/8)
wrapOutputStream (OutputStream, String, String): OutputStream 100% (1/1)85%  (52/61)80%  (12/15)
getCompressor (String): Compressor 100% (1/1)94%  (32/34)91%  (10/11)
CompressTool (): void 100% (1/1)100% (3/3)100% (2/2)
compress (byte [], String): byte [] 100% (1/1)100% (43/43)100% (9/9)
compress (byte [], int, Compressor, byte []): int 100% (1/1)100% (45/45)100% (9/9)
getBuffer (int): byte [] 100% (1/1)100% (21/21)100% (5/5)
getInstance (): CompressTool 100% (1/1)100% (4/4)100% (1/1)
getVariableIntLength (int): int 100% (1/1)100% (26/26)100% (11/11)
readVariableInt (byte [], int): int 100% (1/1)100% (117/117)100% (10/10)
writeVariableInt (byte [], int, int): int 100% (1/1)100% (168/168)100% (31/31)

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 */
6package org.h2.tools;
7 
8import java.io.IOException;
9import java.io.InputStream;
10import java.io.OutputStream;
11import java.util.zip.DeflaterOutputStream;
12import java.util.zip.GZIPInputStream;
13import java.util.zip.GZIPOutputStream;
14import java.util.zip.InflaterInputStream;
15import java.util.zip.ZipEntry;
16import java.util.zip.ZipInputStream;
17import java.util.zip.ZipOutputStream;
18 
19import org.h2.api.ErrorCode;
20import org.h2.compress.CompressDeflate;
21import org.h2.compress.CompressLZF;
22import org.h2.compress.CompressNo;
23import org.h2.compress.Compressor;
24import org.h2.compress.LZFInputStream;
25import org.h2.compress.LZFOutputStream;
26import org.h2.engine.Constants;
27import org.h2.message.DbException;
28import org.h2.mvstore.DataUtils;
29import org.h2.util.StringUtils;
30 
31/**
32 * A tool to losslessly compress data, and expand the compressed data again.
33 */
34public class CompressTool {
35 
36    private static final int MAX_BUFFER_SIZE =
37            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 DataUtils.newBytes(min);
47        }
48        if (cachedBuffer == null || cachedBuffer.length < min) {
49            cachedBuffer = DataUtils.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 = DataUtils.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,
87            byte[] out) {
88        int newLen = 0;
89        out[0] = (byte) compress.getAlgorithm();
90        int start = 1 + writeVariableInt(out, 1, len);
91        newLen = compress.compress(in, len, out, start);
92        if (newLen > len + start || newLen <= 0) {
93            out[0] = Compressor.NO;
94            System.arraycopy(in, 0, out, start, len);
95            newLen = len + start;
96        }
97        return newLen;
98    }
99 
100    /**
101     * Expands the compressed  data.
102     *
103     * @param in the byte array with the compressed data
104     * @return the uncompressed data
105     */
106    public byte[] expand(byte[] in) {
107        int algorithm = in[0];
108        Compressor compress = getCompressor(algorithm);
109        try {
110            int len = readVariableInt(in, 1);
111            int start = 1 + getVariableIntLength(len);
112            byte[] buff = DataUtils.newBytes(len);
113            compress.expand(in, start, in.length - start, buff, 0, len);
114            return buff;
115        } catch (Exception e) {
116            throw DbException.get(ErrorCode.COMPRESSION_ERROR, e);
117        }
118    }
119 
120    /**
121     * INTERNAL
122     */
123    public static void expand(byte[] in, byte[] out, int outPos) {
124        int algorithm = in[0];
125        Compressor compress = getCompressor(algorithm);
126        try {
127            int len = readVariableInt(in, 1);
128            int start = 1 + getVariableIntLength(len);
129            compress.expand(in, start, in.length - start, out, outPos, len);
130        } catch (Exception e) {
131            throw DbException.get(ErrorCode.COMPRESSION_ERROR, e);
132        }
133    }
134 
135    /**
136     * Read a variable size integer using Rice coding.
137     *
138     * @param buff the buffer
139     * @param pos the position
140     * @return the integer
141     */
142    public static int readVariableInt(byte[] buff, int pos) {
143        int x = buff[pos++] & 0xff;
144        if (x < 0x80) {
145            return x;
146        }
147        if (x < 0xc0) {
148            return ((x & 0x3f) << 8) + (buff[pos] & 0xff);
149        }
150        if (x < 0xe0) {
151            return ((x & 0x1f) << 16) +
152                    ((buff[pos++] & 0xff) << 8) +
153                    (buff[pos] & 0xff);
154        }
155        if (x < 0xf0) {
156            return ((x & 0xf) << 24) +
157                    ((buff[pos++] & 0xff) << 16) +
158                    ((buff[pos++] & 0xff) << 8) +
159                    (buff[pos] & 0xff);
160        }
161        return ((buff[pos++] & 0xff) << 24) +
162                ((buff[pos++] & 0xff) << 16) +
163                ((buff[pos++] & 0xff) << 8) +
164                (buff[pos] & 0xff);
165    }
166 
167    /**
168     * Write a variable size integer using Rice coding.
169     * Negative values need 5 bytes.
170     *
171     * @param buff the buffer
172     * @param pos the position
173     * @param x the value
174     * @return the number of bytes written (0-5)
175     */
176    public static int writeVariableInt(byte[] buff, int pos, int x) {
177        if (x < 0) {
178            buff[pos++] = (byte) 0xf0;
179            buff[pos++] = (byte) (x >> 24);
180            buff[pos++] = (byte) (x >> 16);
181            buff[pos++] = (byte) (x >> 8);
182            buff[pos] = (byte) x;
183            return 5;
184        } else if (x < 0x80) {
185            buff[pos] = (byte) x;
186            return 1;
187        } else if (x < 0x4000) {
188            buff[pos++] = (byte) (0x80 | (x >> 8));
189            buff[pos] = (byte) x;
190            return 2;
191        } else if (x < 0x200000) {
192            buff[pos++] = (byte) (0xc0 | (x >> 16));
193            buff[pos++] = (byte) (x >> 8);
194            buff[pos] = (byte) x;
195            return 3;
196        } else if (x < 0x10000000) {
197            buff[pos++] = (byte) (0xe0 | (x >> 24));
198            buff[pos++] = (byte) (x >> 16);
199            buff[pos++] = (byte) (x >> 8);
200            buff[pos] = (byte) x;
201            return 4;
202        } else {
203            buff[pos++] = (byte) 0xf0;
204            buff[pos++] = (byte) (x >> 24);
205            buff[pos++] = (byte) (x >> 16);
206            buff[pos++] = (byte) (x >> 8);
207            buff[pos] = (byte) x;
208            return 5;
209        }
210    }
211 
212    /**
213     * Get a variable size integer length using Rice coding.
214     * Negative values need 5 bytes.
215     *
216     * @param x the value
217     * @return the number of bytes needed (0-5)
218     */
219    public static int getVariableIntLength(int x) {
220        if (x < 0) {
221            return 5;
222        } else if (x < 0x80) {
223            return 1;
224        } else if (x < 0x4000) {
225            return 2;
226        } else if (x < 0x200000) {
227            return 3;
228        } else if (x < 0x10000000) {
229            return 4;
230        } else {
231            return 5;
232        }
233    }
234 
235    private static Compressor getCompressor(String algorithm) {
236        if (algorithm == null) {
237            algorithm = "LZF";
238        }
239        int idx = algorithm.indexOf(' ');
240        String options = null;
241        if (idx > 0) {
242            options = algorithm.substring(idx + 1);
243            algorithm = algorithm.substring(0, idx);
244        }
245        int a = getCompressAlgorithm(algorithm);
246        Compressor compress = getCompressor(a);
247        compress.setOptions(options);
248        return compress;
249    }
250 
251    /**
252     * INTERNAL
253     */
254    public static int getCompressAlgorithm(String algorithm) {
255        algorithm = StringUtils.toUpperEnglish(algorithm);
256        if ("NO".equals(algorithm)) {
257            return Compressor.NO;
258        } else if ("LZF".equals(algorithm)) {
259            return Compressor.LZF;
260        } else if ("DEFLATE".equals(algorithm)) {
261            return Compressor.DEFLATE;
262        } else {
263            throw DbException.get(
264                    ErrorCode.UNSUPPORTED_COMPRESSION_ALGORITHM_1,
265                    algorithm);
266        }
267    }
268 
269    private static Compressor getCompressor(int algorithm) {
270        switch (algorithm) {
271        case Compressor.NO:
272            return new CompressNo();
273        case Compressor.LZF:
274            return new CompressLZF();
275        case Compressor.DEFLATE:
276            return new CompressDeflate();
277        default:
278            throw DbException.get(
279                    ErrorCode.UNSUPPORTED_COMPRESSION_ALGORITHM_1,
280                    "" + algorithm);
281        }
282    }
283 
284    /**
285     * INTERNAL
286     */
287    public static OutputStream wrapOutputStream(OutputStream out,
288            String compressionAlgorithm, String entryName) {
289        try {
290            if ("GZIP".equals(compressionAlgorithm)) {
291                out = new GZIPOutputStream(out);
292            } else if ("ZIP".equals(compressionAlgorithm)) {
293                ZipOutputStream z = new ZipOutputStream(out);
294                z.putNextEntry(new ZipEntry(entryName));
295                out = z;
296            } else if ("DEFLATE".equals(compressionAlgorithm)) {
297                out = new DeflaterOutputStream(out);
298            } else if ("LZF".equals(compressionAlgorithm)) {
299                out = new LZFOutputStream(out);
300            } else if (compressionAlgorithm != null) {
301                throw DbException.get(
302                        ErrorCode.UNSUPPORTED_COMPRESSION_ALGORITHM_1,
303                        compressionAlgorithm);
304            }
305            return out;
306        } catch (IOException e) {
307            throw DbException.convertIOException(e, null);
308        }
309    }
310 
311    /**
312     * INTERNAL
313     */
314    public static InputStream wrapInputStream(InputStream in,
315            String compressionAlgorithm, String entryName) {
316        try {
317            if ("GZIP".equals(compressionAlgorithm)) {
318                in = new GZIPInputStream(in);
319            } else if ("ZIP".equals(compressionAlgorithm)) {
320                ZipInputStream z = new ZipInputStream(in);
321                while (true) {
322                    ZipEntry entry = z.getNextEntry();
323                    if (entry == null) {
324                        return null;
325                    }
326                    if (entryName.equals(entry.getName())) {
327                        break;
328                    }
329                }
330                in = z;
331            } else if ("DEFLATE".equals(compressionAlgorithm)) {
332                in = new InflaterInputStream(in);
333            } else if ("LZF".equals(compressionAlgorithm)) {
334                in = new LZFInputStream(in);
335            } else if (compressionAlgorithm != null) {
336                throw DbException.get(
337                        ErrorCode.UNSUPPORTED_COMPRESSION_ALGORITHM_1,
338                        compressionAlgorithm);
339            }
340            return in;
341        } catch (IOException e) {
342            throw DbException.convertIOException(e, null);
343        }
344    }
345 
346}
347 

[all classes][org.h2.tools]
EMMA 2.0.5312 (C) Vladimir Roubtsov