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.InputStream; |
10 | import org.h2.message.DbException; |
11 | import org.h2.mvstore.DataUtils; |
12 | |
13 | /** |
14 | * An input stream to read from an LZF stream. |
15 | * The data is automatically expanded. |
16 | */ |
17 | public class LZFInputStream extends InputStream { |
18 | |
19 | private final InputStream in; |
20 | private CompressLZF decompress = new CompressLZF(); |
21 | private int pos; |
22 | private int bufferLength; |
23 | private byte[] inBuffer; |
24 | private byte[] buffer; |
25 | |
26 | public LZFInputStream(InputStream in) throws IOException { |
27 | this.in = in; |
28 | if (readInt() != LZFOutputStream.MAGIC) { |
29 | throw new IOException("Not an LZFInputStream"); |
30 | } |
31 | } |
32 | |
33 | private static byte[] ensureSize(byte[] buff, int len) { |
34 | return buff == null || buff.length < len ? DataUtils.newBytes(len) : buff; |
35 | } |
36 | |
37 | private void fillBuffer() throws IOException { |
38 | if (buffer != null && pos < bufferLength) { |
39 | return; |
40 | } |
41 | int len = readInt(); |
42 | if (decompress == null) { |
43 | // EOF |
44 | this.bufferLength = 0; |
45 | } else if (len < 0) { |
46 | len = -len; |
47 | buffer = ensureSize(buffer, len); |
48 | readFully(buffer, len); |
49 | this.bufferLength = len; |
50 | } else { |
51 | inBuffer = ensureSize(inBuffer, len); |
52 | int size = readInt(); |
53 | readFully(inBuffer, len); |
54 | buffer = ensureSize(buffer, size); |
55 | try { |
56 | decompress.expand(inBuffer, 0, len, buffer, 0, size); |
57 | } catch (ArrayIndexOutOfBoundsException e) { |
58 | DbException.convertToIOException(e); |
59 | } |
60 | this.bufferLength = size; |
61 | } |
62 | pos = 0; |
63 | } |
64 | |
65 | private void readFully(byte[] buff, int len) throws IOException { |
66 | int off = 0; |
67 | while (len > 0) { |
68 | int l = in.read(buff, off, len); |
69 | len -= l; |
70 | off += l; |
71 | } |
72 | } |
73 | |
74 | private int readInt() throws IOException { |
75 | int x = in.read(); |
76 | if (x < 0) { |
77 | decompress = null; |
78 | return 0; |
79 | } |
80 | x = (x << 24) + (in.read() << 16) + (in.read() << 8) + in.read(); |
81 | return x; |
82 | } |
83 | |
84 | @Override |
85 | public int read() throws IOException { |
86 | fillBuffer(); |
87 | if (pos >= bufferLength) { |
88 | return -1; |
89 | } |
90 | return buffer[pos++] & 255; |
91 | } |
92 | |
93 | @Override |
94 | public int read(byte[] b) throws IOException { |
95 | return read(b, 0, b.length); |
96 | } |
97 | |
98 | @Override |
99 | public int read(byte[] b, int off, int len) throws IOException { |
100 | if (len == 0) { |
101 | return 0; |
102 | } |
103 | int read = 0; |
104 | while (len > 0) { |
105 | int r = readBlock(b, off, len); |
106 | if (r < 0) { |
107 | break; |
108 | } |
109 | read += r; |
110 | off += r; |
111 | len -= r; |
112 | } |
113 | return read == 0 ? -1 : read; |
114 | } |
115 | |
116 | private int readBlock(byte[] b, int off, int len) throws IOException { |
117 | fillBuffer(); |
118 | if (pos >= bufferLength) { |
119 | return -1; |
120 | } |
121 | int max = Math.min(len, bufferLength - pos); |
122 | max = Math.min(max, b.length - off); |
123 | System.arraycopy(buffer, pos, b, off, max); |
124 | pos += max; |
125 | return max; |
126 | } |
127 | |
128 | @Override |
129 | public void close() throws IOException { |
130 | in.close(); |
131 | } |
132 | |
133 | } |