1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.compression;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.channel.ChannelHandlerContext;
20 import io.netty.handler.codec.ByteToMessageDecoder;
21
22 import java.util.List;
23
24 import static io.netty.handler.codec.compression.Bzip2Constants.BASE_BLOCK_SIZE;
25 import static io.netty.handler.codec.compression.Bzip2Constants.BLOCK_HEADER_MAGIC_1;
26 import static io.netty.handler.codec.compression.Bzip2Constants.BLOCK_HEADER_MAGIC_2;
27 import static io.netty.handler.codec.compression.Bzip2Constants.END_OF_STREAM_MAGIC_1;
28 import static io.netty.handler.codec.compression.Bzip2Constants.END_OF_STREAM_MAGIC_2;
29 import static io.netty.handler.codec.compression.Bzip2Constants.HUFFMAN_MAXIMUM_TABLES;
30 import static io.netty.handler.codec.compression.Bzip2Constants.HUFFMAN_MAX_ALPHABET_SIZE;
31 import static io.netty.handler.codec.compression.Bzip2Constants.HUFFMAN_MINIMUM_TABLES;
32 import static io.netty.handler.codec.compression.Bzip2Constants.HUFFMAN_SELECTOR_LIST_MAX_LENGTH;
33 import static io.netty.handler.codec.compression.Bzip2Constants.HUFFMAN_SYMBOL_RANGE_SIZE;
34 import static io.netty.handler.codec.compression.Bzip2Constants.MAGIC_NUMBER;
35 import static io.netty.handler.codec.compression.Bzip2Constants.MAX_BLOCK_SIZE;
36 import static io.netty.handler.codec.compression.Bzip2Constants.MAX_SELECTORS;
37 import static io.netty.handler.codec.compression.Bzip2Constants.MIN_BLOCK_SIZE;
38
39
40
41
42
43
44 public class Bzip2Decoder extends ByteToMessageDecoder {
45
46
47
48 private enum State {
49 INIT,
50 INIT_BLOCK,
51 INIT_BLOCK_PARAMS,
52 RECEIVE_HUFFMAN_USED_MAP,
53 RECEIVE_HUFFMAN_USED_BITMAPS,
54 RECEIVE_SELECTORS_NUMBER,
55 RECEIVE_SELECTORS,
56 RECEIVE_HUFFMAN_LENGTH,
57 DECODE_HUFFMAN_DATA,
58 EOF
59 }
60 private State currentState = State.INIT;
61
62
63
64
65 private final Bzip2BitReader reader = new Bzip2BitReader();
66
67
68
69
70 private Bzip2BlockDecompressor blockDecompressor;
71
72
73
74
75 private Bzip2HuffmanStageDecoder huffmanStageDecoder;
76
77
78
79
80 private int blockSize;
81
82
83
84
85 private int blockCRC;
86
87
88
89
90 private int streamCRC;
91
92 @Override
93 protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
94 if (!in.isReadable()) {
95 return;
96 }
97
98 final Bzip2BitReader reader = this.reader;
99 reader.setByteBuf(in);
100
101 for (;;) {
102 switch (currentState) {
103 case INIT:
104 if (in.readableBytes() < 4) {
105 return;
106 }
107 int magicNumber = in.readUnsignedMedium();
108 if (magicNumber != MAGIC_NUMBER) {
109 throw new DecompressionException("Unexpected stream identifier contents. Mismatched bzip2 " +
110 "protocol version?");
111 }
112 int blockSize = in.readByte() - '0';
113 if (blockSize < MIN_BLOCK_SIZE || blockSize > MAX_BLOCK_SIZE) {
114 throw new DecompressionException("block size is invalid");
115 }
116 this.blockSize = blockSize * BASE_BLOCK_SIZE;
117
118 streamCRC = 0;
119 currentState = State.INIT_BLOCK;
120
121 case INIT_BLOCK:
122 if (!reader.hasReadableBytes(10)) {
123 return;
124 }
125
126 final int magic1 = reader.readBits(24);
127 final int magic2 = reader.readBits(24);
128 if (magic1 == END_OF_STREAM_MAGIC_1 && magic2 == END_OF_STREAM_MAGIC_2) {
129
130 final int storedCombinedCRC = reader.readInt();
131 if (storedCombinedCRC != streamCRC) {
132 throw new DecompressionException("stream CRC error");
133 }
134 currentState = State.EOF;
135 break;
136 }
137 if (magic1 != BLOCK_HEADER_MAGIC_1 || magic2 != BLOCK_HEADER_MAGIC_2) {
138 throw new DecompressionException("bad block header");
139 }
140 blockCRC = reader.readInt();
141 currentState = State.INIT_BLOCK_PARAMS;
142
143 case INIT_BLOCK_PARAMS:
144 if (!reader.hasReadableBits(25)) {
145 return;
146 }
147 final boolean blockRandomised = reader.readBoolean();
148 final int bwtStartPointer = reader.readBits(24);
149
150 blockDecompressor = new Bzip2BlockDecompressor(this.blockSize, blockCRC,
151 blockRandomised, bwtStartPointer, reader);
152 currentState = State.RECEIVE_HUFFMAN_USED_MAP;
153
154 case RECEIVE_HUFFMAN_USED_MAP:
155 if (!reader.hasReadableBits(16)) {
156 return;
157 }
158 blockDecompressor.huffmanInUse16 = reader.readBits(16);
159 currentState = State.RECEIVE_HUFFMAN_USED_BITMAPS;
160
161 case RECEIVE_HUFFMAN_USED_BITMAPS:
162 Bzip2BlockDecompressor blockDecompressor = this.blockDecompressor;
163 final int inUse16 = blockDecompressor.huffmanInUse16;
164 final int bitNumber = Integer.bitCount(inUse16);
165 final byte[] huffmanSymbolMap = blockDecompressor.huffmanSymbolMap;
166
167 if (!reader.hasReadableBits(bitNumber * HUFFMAN_SYMBOL_RANGE_SIZE + 3)) {
168 return;
169 }
170
171 int huffmanSymbolCount = 0;
172 if (bitNumber > 0) {
173 for (int i = 0; i < 16; i++) {
174 if ((inUse16 & 1 << 15 >>> i) != 0) {
175 for (int j = 0, k = i << 4; j < HUFFMAN_SYMBOL_RANGE_SIZE; j++, k++) {
176 if (reader.readBoolean()) {
177 huffmanSymbolMap[huffmanSymbolCount++] = (byte) k;
178 }
179 }
180 }
181 }
182 }
183 blockDecompressor.huffmanEndOfBlockSymbol = huffmanSymbolCount + 1;
184
185 int totalTables = reader.readBits(3);
186 if (totalTables < HUFFMAN_MINIMUM_TABLES || totalTables > HUFFMAN_MAXIMUM_TABLES) {
187 throw new DecompressionException("incorrect huffman groups number");
188 }
189 int alphaSize = huffmanSymbolCount + 2;
190 if (alphaSize > HUFFMAN_MAX_ALPHABET_SIZE) {
191 throw new DecompressionException("incorrect alphabet size");
192 }
193 huffmanStageDecoder = new Bzip2HuffmanStageDecoder(reader, totalTables, alphaSize);
194 currentState = State.RECEIVE_SELECTORS_NUMBER;
195
196 case RECEIVE_SELECTORS_NUMBER:
197 if (!reader.hasReadableBits(15)) {
198 return;
199 }
200 int totalSelectors = reader.readBits(15);
201 if (totalSelectors < 1 || totalSelectors > MAX_SELECTORS) {
202 throw new DecompressionException("incorrect selectors number");
203 }
204 huffmanStageDecoder.selectors = new byte[totalSelectors];
205
206 currentState = State.RECEIVE_SELECTORS;
207
208 case RECEIVE_SELECTORS:
209 Bzip2HuffmanStageDecoder huffmanStageDecoder = this.huffmanStageDecoder;
210 byte[] selectors = huffmanStageDecoder.selectors;
211 totalSelectors = selectors.length;
212 final Bzip2MoveToFrontTable tableMtf = huffmanStageDecoder.tableMTF;
213
214 int currSelector;
215
216 for (currSelector = huffmanStageDecoder.currentSelector;
217 currSelector < totalSelectors; currSelector++) {
218 if (!reader.hasReadableBits(HUFFMAN_SELECTOR_LIST_MAX_LENGTH)) {
219
220 huffmanStageDecoder.currentSelector = currSelector;
221 return;
222 }
223 int index = 0;
224 while (reader.readBoolean()) {
225 index++;
226 if (index >= huffmanStageDecoder.totalTables) {
227 throw new DecompressionException("incorrect selector index " + index);
228 }
229 }
230 selectors[currSelector] = tableMtf.indexToFront(index);
231 }
232
233 currentState = State.RECEIVE_HUFFMAN_LENGTH;
234
235 case RECEIVE_HUFFMAN_LENGTH:
236 huffmanStageDecoder = this.huffmanStageDecoder;
237 totalTables = huffmanStageDecoder.totalTables;
238 final byte[][] codeLength = huffmanStageDecoder.tableCodeLengths;
239 alphaSize = huffmanStageDecoder.alphabetSize;
240
241
242 int currGroup;
243 int currLength = huffmanStageDecoder.currentLength;
244 int currAlpha = 0;
245 boolean modifyLength = huffmanStageDecoder.modifyLength;
246 boolean saveStateAndReturn = false;
247 loop: for (currGroup = huffmanStageDecoder.currentGroup; currGroup < totalTables; currGroup++) {
248
249 if (!reader.hasReadableBits(5)) {
250 saveStateAndReturn = true;
251 break;
252 }
253 if (currLength < 0) {
254 currLength = reader.readBits(5);
255 }
256 for (currAlpha = huffmanStageDecoder.currentAlpha; currAlpha < alphaSize; currAlpha++) {
257
258 if (!reader.isReadable()) {
259 saveStateAndReturn = true;
260 break loop;
261 }
262 while (modifyLength || reader.readBoolean()) {
263 if (!reader.isReadable()) {
264 modifyLength = true;
265 saveStateAndReturn = true;
266 break loop;
267 }
268
269 currLength += reader.readBoolean() ? -1 : 1;
270 modifyLength = false;
271 if (!reader.isReadable()) {
272 saveStateAndReturn = true;
273 break loop;
274 }
275 }
276 codeLength[currGroup][currAlpha] = (byte) currLength;
277 }
278 currLength = -1;
279 currAlpha = huffmanStageDecoder.currentAlpha = 0;
280 modifyLength = false;
281 }
282 if (saveStateAndReturn) {
283
284 huffmanStageDecoder.currentGroup = currGroup;
285 huffmanStageDecoder.currentLength = currLength;
286 huffmanStageDecoder.currentAlpha = currAlpha;
287 huffmanStageDecoder.modifyLength = modifyLength;
288 return;
289 }
290
291
292 huffmanStageDecoder.createHuffmanDecodingTables();
293 currentState = State.DECODE_HUFFMAN_DATA;
294
295 case DECODE_HUFFMAN_DATA:
296 blockDecompressor = this.blockDecompressor;
297 final int oldReaderIndex = in.readerIndex();
298 final boolean decoded = blockDecompressor.decodeHuffmanData(this.huffmanStageDecoder);
299 if (!decoded) {
300 return;
301 }
302
303
304
305 if (in.readerIndex() == oldReaderIndex && in.isReadable()) {
306 reader.refill();
307 }
308
309 final int blockLength = blockDecompressor.blockLength();
310 ByteBuf uncompressed = ctx.alloc().buffer(blockLength);
311 try {
312 int uncByte;
313 while ((uncByte = blockDecompressor.read()) >= 0) {
314 uncompressed.writeByte(uncByte);
315 }
316
317 currentState = State.INIT_BLOCK;
318 int currentBlockCRC = blockDecompressor.checkCRC();
319 streamCRC = (streamCRC << 1 | streamCRC >>> 31) ^ currentBlockCRC;
320
321 out.add(uncompressed);
322 uncompressed = null;
323 } finally {
324 if (uncompressed != null) {
325 uncompressed.release();
326 }
327 }
328
329
330 return;
331 case EOF:
332 in.skipBytes(in.readableBytes());
333 return;
334 default:
335 throw new IllegalStateException();
336 }
337 }
338 }
339
340
341
342
343
344 public boolean isClosed() {
345 return currentState == State.EOF;
346 }
347 }