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.io.IOException; |
9 | import java.io.OutputStream; |
10 | import org.h2.engine.Constants; |
11 | |
12 | /** |
13 | * An output stream to write an LZF stream. |
14 | * The data is automatically compressed. |
15 | */ |
16 | public class LZFOutputStream extends OutputStream { |
17 | |
18 | /** |
19 | * The file header of a LZF file. |
20 | */ |
21 | static final int MAGIC = ('H' << 24) | ('2' << 16) | ('I' << 8) | 'S'; |
22 | |
23 | private final OutputStream out; |
24 | private final CompressLZF compress = new CompressLZF(); |
25 | private final byte[] buffer; |
26 | private int pos; |
27 | private byte[] outBuffer; |
28 | |
29 | public LZFOutputStream(OutputStream out) throws IOException { |
30 | this.out = out; |
31 | int len = Constants.IO_BUFFER_SIZE_COMPRESS; |
32 | buffer = new byte[len]; |
33 | ensureOutput(len); |
34 | writeInt(MAGIC); |
35 | } |
36 | |
37 | private void ensureOutput(int len) { |
38 | // TODO calculate the maximum overhead (worst case) for the output |
39 | // buffer |
40 | int outputLen = (len < 100 ? len + 100 : len) * 2; |
41 | if (outBuffer == null || outBuffer.length < outputLen) { |
42 | outBuffer = new byte[outputLen]; |
43 | } |
44 | } |
45 | |
46 | @Override |
47 | public void write(int b) throws IOException { |
48 | if (pos >= buffer.length) { |
49 | flush(); |
50 | } |
51 | buffer[pos++] = (byte) b; |
52 | } |
53 | |
54 | private void compressAndWrite(byte[] buff, int len) throws IOException { |
55 | if (len > 0) { |
56 | ensureOutput(len); |
57 | int compressed = compress.compress(buff, len, outBuffer, 0); |
58 | if (compressed > len) { |
59 | writeInt(-len); |
60 | out.write(buff, 0, len); |
61 | } else { |
62 | writeInt(compressed); |
63 | writeInt(len); |
64 | out.write(outBuffer, 0, compressed); |
65 | } |
66 | } |
67 | } |
68 | |
69 | private void writeInt(int x) throws IOException { |
70 | out.write((byte) (x >> 24)); |
71 | out.write((byte) (x >> 16)); |
72 | out.write((byte) (x >> 8)); |
73 | out.write((byte) x); |
74 | } |
75 | |
76 | @Override |
77 | public void write(byte[] buff, int off, int len) throws IOException { |
78 | while (len > 0) { |
79 | int copy = Math.min(buffer.length - pos, len); |
80 | System.arraycopy(buff, off, buffer, pos, copy); |
81 | pos += copy; |
82 | if (pos >= buffer.length) { |
83 | flush(); |
84 | } |
85 | off += copy; |
86 | len -= copy; |
87 | } |
88 | } |
89 | |
90 | @Override |
91 | public void flush() throws IOException { |
92 | compressAndWrite(buffer, pos); |
93 | pos = 0; |
94 | } |
95 | |
96 | @Override |
97 | public void close() throws IOException { |
98 | flush(); |
99 | out.close(); |
100 | } |
101 | |
102 | } |