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