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
20 import static io.netty.handler.codec.compression.Bzip2Constants.*;
21
22
23
24
25
26
27
28
29
30
31
32
33
34 final class Bzip2BlockCompressor {
35
36
37
38 private final Bzip2BitWriter writer;
39
40
41
42
43 private final Crc32 crc = new Crc32();
44
45
46
47
48 private final byte[] block;
49
50
51
52
53 private int blockLength;
54
55
56
57
58 private final int blockLengthLimit;
59
60
61
62
63
64 private final boolean[] blockValuesPresent = new boolean[256];
65
66
67
68
69 private final int[] bwtBlock;
70
71
72
73
74 private int rleCurrentValue = -1;
75
76
77
78
79 private int rleLength;
80
81
82
83
84
85
86 Bzip2BlockCompressor(final Bzip2BitWriter writer, final int blockSize) {
87 this.writer = writer;
88
89
90 block = new byte[blockSize + 1];
91 bwtBlock = new int[blockSize + 1];
92 blockLengthLimit = blockSize - 6;
93 }
94
95
96
97
98 private void writeSymbolMap(ByteBuf out) {
99 Bzip2BitWriter writer = this.writer;
100
101 final boolean[] blockValuesPresent = this.blockValuesPresent;
102 final boolean[] condensedInUse = new boolean[16];
103
104 for (int i = 0; i < condensedInUse.length; i++) {
105 for (int j = 0, k = i << 4; j < HUFFMAN_SYMBOL_RANGE_SIZE; j++, k++) {
106 if (blockValuesPresent[k]) {
107 condensedInUse[i] = true;
108 }
109 }
110 }
111
112 for (int i = 0; i < condensedInUse.length; i++) {
113 writer.writeBoolean(out, condensedInUse[i]);
114 }
115
116 for (int i = 0; i < condensedInUse.length; i++) {
117 if (condensedInUse[i]) {
118 for (int j = 0, k = i << 4; j < HUFFMAN_SYMBOL_RANGE_SIZE; j++, k++) {
119 writer.writeBoolean(out, blockValuesPresent[k]);
120 }
121 }
122 }
123 }
124
125
126
127
128
129
130 private void writeRun(final int value, int runLength) {
131 final int blockLength = this.blockLength;
132 final byte[] block = this.block;
133
134 blockValuesPresent[value] = true;
135 crc.updateCRC(value, runLength);
136
137 final byte byteValue = (byte) value;
138 switch (runLength) {
139 case 1:
140 block[blockLength] = byteValue;
141 this.blockLength = blockLength + 1;
142 break;
143 case 2:
144 block[blockLength] = byteValue;
145 block[blockLength + 1] = byteValue;
146 this.blockLength = blockLength + 2;
147 break;
148 case 3:
149 block[blockLength] = byteValue;
150 block[blockLength + 1] = byteValue;
151 block[blockLength + 2] = byteValue;
152 this.blockLength = blockLength + 3;
153 break;
154 default:
155 runLength -= 4;
156 blockValuesPresent[runLength] = true;
157 block[blockLength] = byteValue;
158 block[blockLength + 1] = byteValue;
159 block[blockLength + 2] = byteValue;
160 block[blockLength + 3] = byteValue;
161 block[blockLength + 4] = (byte) runLength;
162 this.blockLength = blockLength + 5;
163 break;
164 }
165 }
166
167
168
169
170
171
172 boolean write(final int value) {
173 if (blockLength > blockLengthLimit) {
174 return false;
175 }
176 final int rleCurrentValue = this.rleCurrentValue;
177 final int rleLength = this.rleLength;
178
179 if (rleLength == 0) {
180 this.rleCurrentValue = value;
181 this.rleLength = 1;
182 } else if (rleCurrentValue != value) {
183
184 writeRun(rleCurrentValue & 0xff, rleLength);
185 this.rleCurrentValue = value;
186 this.rleLength = 1;
187 } else {
188 if (rleLength == 254) {
189 writeRun(rleCurrentValue & 0xff, 255);
190 this.rleLength = 0;
191 } else {
192 this.rleLength = rleLength + 1;
193 }
194 }
195 return true;
196 }
197
198
199
200
201
202
203
204
205
206 int write(final byte[] data, int offset, int length) {
207 int written = 0;
208
209 while (length-- > 0) {
210 if (!write(data[offset++])) {
211 break;
212 }
213 written++;
214 }
215 return written;
216 }
217
218
219
220
221 void close(ByteBuf out) {
222
223 if (rleLength > 0) {
224 writeRun(rleCurrentValue & 0xff, rleLength);
225 }
226
227
228 block[blockLength] = block[0];
229
230
231 Bzip2DivSufSort divSufSort = new Bzip2DivSufSort(block, bwtBlock, blockLength);
232 int bwtStartPointer = divSufSort.bwt();
233
234 Bzip2BitWriter writer = this.writer;
235
236
237 writer.writeBits(out, 24, BLOCK_HEADER_MAGIC_1);
238 writer.writeBits(out, 24, BLOCK_HEADER_MAGIC_2);
239 writer.writeInt(out, crc.getCRC());
240 writer.writeBoolean(out, false);
241 writer.writeBits(out, 24, bwtStartPointer);
242
243
244 writeSymbolMap(out);
245
246
247 Bzip2MTFAndRLE2StageEncoder mtfEncoder = new Bzip2MTFAndRLE2StageEncoder(bwtBlock, blockLength,
248 blockValuesPresent);
249 mtfEncoder.encode();
250
251
252 Bzip2HuffmanStageEncoder huffmanEncoder = new Bzip2HuffmanStageEncoder(writer,
253 mtfEncoder.mtfBlock(),
254 mtfEncoder.mtfLength(),
255 mtfEncoder.mtfAlphabetSize(),
256 mtfEncoder.mtfSymbolFrequencies());
257 huffmanEncoder.encode(out);
258 }
259
260
261
262
263
264 int availableSize() {
265 if (blockLength == 0) {
266 return blockLengthLimit + 2;
267 }
268 return blockLengthLimit - blockLength + 1;
269 }
270
271
272
273
274
275 boolean isFull() {
276 return blockLength > blockLengthLimit;
277 }
278
279
280
281
282
283 boolean isEmpty() {
284 return blockLength == 0 && rleLength == 0;
285 }
286
287
288
289
290
291 int crc() {
292 return crc.getCRC();
293 }
294 }