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