1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.handler.codec.compression;
17
18 import static io.netty5.handler.codec.compression.Bzip2Constants.HUFFMAN_DECODE_MAX_CODE_LENGTH;
19 import static io.netty5.handler.codec.compression.Bzip2Constants.HUFFMAN_SYMBOL_RUNA;
20 import static io.netty5.handler.codec.compression.Bzip2Constants.HUFFMAN_SYMBOL_RUNB;
21 import static io.netty5.handler.codec.compression.Bzip2Constants.MAX_BLOCK_LENGTH;
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 final class Bzip2BlockDecompressor {
37
38
39
40 private final Bzip2BitReader reader;
41
42
43
44
45 private final Crc32 crc = new Crc32();
46
47
48
49
50 private final int blockCRC;
51
52
53
54
55 private final boolean blockRandomised;
56
57
58
59
60
61 int huffmanEndOfBlockSymbol;
62
63
64
65
66 int huffmanInUse16;
67
68
69
70
71
72
73 final byte[] huffmanSymbolMap = new byte[256];
74
75
76
77
78
79
80 private final int[] bwtByteCounts = new int[256];
81
82
83
84
85
86 private final byte[] bwtBlock;
87
88
89
90
91 private final int bwtStartPointer;
92
93
94
95
96
97
98
99
100
101
102
103 private int[] bwtMergedPointers;
104
105
106
107
108 private int bwtCurrentMergedPointer;
109
110
111
112
113
114 private int bwtBlockLength;
115
116
117
118
119 private int bwtBytesDecoded;
120
121
122
123
124
125 private int rleLastDecodedByte = -1;
126
127
128
129
130
131 private int rleAccumulator;
132
133
134
135
136 private int rleRepeat;
137
138
139
140
141 private int randomIndex;
142
143
144
145
146 private int randomCount = Bzip2Rand.rNums(0) - 1;
147
148
149
150
151 private final Bzip2MoveToFrontTable symbolMTF = new Bzip2MoveToFrontTable();
152
153
154 private int repeatCount;
155 private int repeatIncrement = 1;
156 private int mtfValue;
157
158 Bzip2BlockDecompressor(final int blockSize, final int blockCRC, final boolean blockRandomised,
159 final int bwtStartPointer, final Bzip2BitReader reader) {
160
161 bwtBlock = new byte[blockSize];
162
163 this.blockCRC = blockCRC;
164 this.blockRandomised = blockRandomised;
165 this.bwtStartPointer = bwtStartPointer;
166
167 this.reader = reader;
168 }
169
170
171
172
173
174 boolean decodeHuffmanData(final Bzip2HuffmanStageDecoder huffmanDecoder) {
175 final Bzip2BitReader reader = this.reader;
176 final byte[] bwtBlock = this.bwtBlock;
177 final byte[] huffmanSymbolMap = this.huffmanSymbolMap;
178 final int streamBlockSize = this.bwtBlock.length;
179 final int huffmanEndOfBlockSymbol = this.huffmanEndOfBlockSymbol;
180 final int[] bwtByteCounts = this.bwtByteCounts;
181 final Bzip2MoveToFrontTable symbolMTF = this.symbolMTF;
182
183 int bwtBlockLength = this.bwtBlockLength;
184 int repeatCount = this.repeatCount;
185 int repeatIncrement = this.repeatIncrement;
186 int mtfValue = this.mtfValue;
187
188 for (;;) {
189 if (!reader.hasReadableBits(HUFFMAN_DECODE_MAX_CODE_LENGTH)) {
190 this.bwtBlockLength = bwtBlockLength;
191 this.repeatCount = repeatCount;
192 this.repeatIncrement = repeatIncrement;
193 this.mtfValue = mtfValue;
194 return false;
195 }
196 final int nextSymbol = huffmanDecoder.nextSymbol();
197
198 if (nextSymbol == HUFFMAN_SYMBOL_RUNA) {
199 repeatCount += repeatIncrement;
200 repeatIncrement <<= 1;
201 } else if (nextSymbol == HUFFMAN_SYMBOL_RUNB) {
202 repeatCount += repeatIncrement << 1;
203 repeatIncrement <<= 1;
204 } else {
205 if (repeatCount > 0) {
206 if (bwtBlockLength + repeatCount > streamBlockSize) {
207 throw new DecompressionException("block exceeds declared block size");
208 }
209 final byte nextByte = huffmanSymbolMap[mtfValue];
210 bwtByteCounts[nextByte & 0xff] += repeatCount;
211 while (--repeatCount >= 0) {
212 bwtBlock[bwtBlockLength++] = nextByte;
213 }
214
215 repeatCount = 0;
216 repeatIncrement = 1;
217 }
218
219 if (nextSymbol == huffmanEndOfBlockSymbol) {
220 break;
221 }
222
223 if (bwtBlockLength >= streamBlockSize) {
224 throw new DecompressionException("block exceeds declared block size");
225 }
226
227 mtfValue = symbolMTF.indexToFront(nextSymbol - 1) & 0xff;
228
229 final byte nextByte = huffmanSymbolMap[mtfValue];
230 bwtByteCounts[nextByte & 0xff]++;
231 bwtBlock[bwtBlockLength++] = nextByte;
232 }
233 }
234 if (bwtBlockLength > MAX_BLOCK_LENGTH) {
235 throw new DecompressionException("block length exceeds max block length: "
236 + bwtBlockLength + " > " + MAX_BLOCK_LENGTH);
237 }
238
239 this.bwtBlockLength = bwtBlockLength;
240 initialiseInverseBWT();
241 return true;
242 }
243
244
245
246
247 private void initialiseInverseBWT() {
248 final int bwtStartPointer = this.bwtStartPointer;
249 final byte[] bwtBlock = this.bwtBlock;
250 final int[] bwtMergedPointers = new int[bwtBlockLength];
251 final int[] characterBase = new int[256];
252
253 if (bwtStartPointer < 0 || bwtStartPointer >= bwtBlockLength) {
254 throw new DecompressionException("start pointer invalid");
255 }
256
257
258 System.arraycopy(bwtByteCounts, 0, characterBase, 1, 255);
259 for (int i = 2; i <= 255; i++) {
260 characterBase[i] += characterBase[i - 1];
261 }
262
263
264
265
266
267 for (int i = 0; i < bwtBlockLength; i++) {
268 int value = bwtBlock[i] & 0xff;
269 bwtMergedPointers[characterBase[value]++] = (i << 8) + value;
270 }
271
272 this.bwtMergedPointers = bwtMergedPointers;
273 bwtCurrentMergedPointer = bwtMergedPointers[bwtStartPointer];
274 }
275
276
277
278
279
280
281 public int read() {
282 while (rleRepeat < 1) {
283 if (bwtBytesDecoded == bwtBlockLength) {
284 return -1;
285 }
286
287 int nextByte = decodeNextBWTByte();
288 if (nextByte != rleLastDecodedByte) {
289
290 rleLastDecodedByte = nextByte;
291 rleRepeat = 1;
292 rleAccumulator = 1;
293 crc.updateCRC(nextByte);
294 } else {
295 if (++rleAccumulator == 4) {
296
297 int rleRepeat = decodeNextBWTByte() + 1;
298 this.rleRepeat = rleRepeat;
299 rleAccumulator = 0;
300 crc.updateCRC(nextByte, rleRepeat);
301 } else {
302 rleRepeat = 1;
303 crc.updateCRC(nextByte);
304 }
305 }
306 }
307 rleRepeat--;
308
309 return rleLastDecodedByte;
310 }
311
312
313
314
315
316
317 private int decodeNextBWTByte() {
318 int mergedPointer = bwtCurrentMergedPointer;
319 int nextDecodedByte = mergedPointer & 0xff;
320 bwtCurrentMergedPointer = bwtMergedPointers[mergedPointer >>> 8];
321
322 if (blockRandomised) {
323 if (--randomCount == 0) {
324 nextDecodedByte ^= 1;
325 randomIndex = (randomIndex + 1) % 512;
326 randomCount = Bzip2Rand.rNums(randomIndex);
327 }
328 }
329 bwtBytesDecoded++;
330
331 return nextDecodedByte;
332 }
333
334 public int blockLength() {
335 return bwtBlockLength;
336 }
337
338
339
340
341
342
343 int checkCRC() {
344 final int computedBlockCRC = crc.getCRC();
345 if (blockCRC != computedBlockCRC) {
346 throw new DecompressionException("block CRC error");
347 }
348 return computedBlockCRC;
349 }
350 }