1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.compress.harmony.pack200;
18
19 import java.io.EOFException;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.List;
25
26 import org.apache.commons.compress.utils.ExactMath;
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 public final class BHSDCodec extends Codec {
64
65
66
67
68
69 private final int b;
70
71
72
73
74 private final int d;
75
76
77
78
79 private final int h;
80
81
82
83
84 private final int l;
85
86
87
88
89 private final int s;
90
91 private long cardinality;
92
93 private final long smallest;
94
95 private final long largest;
96
97
98
99
100 private final long[] powers;
101
102
103
104
105
106
107
108 public BHSDCodec(final int b, final int h) {
109 this(b, h, 0, 0);
110 }
111
112
113
114
115
116
117
118
119 public BHSDCodec(final int b, final int h, final int s) {
120 this(b, h, s, 0);
121 }
122
123
124
125
126
127
128
129
130
131 public BHSDCodec(final int b, final int h, final int s, final int d) {
132 if (b < 1 || b > 5) {
133 throw new IllegalArgumentException("1<=b<=5");
134 }
135 if (h < 1 || h > 256) {
136 throw new IllegalArgumentException("1<=h<=256");
137 }
138 if (s < 0 || s > 2) {
139 throw new IllegalArgumentException("0<=s<=2");
140 }
141 if (d < 0 || d > 1) {
142 throw new IllegalArgumentException("0<=d<=1");
143 }
144 if (b == 1 && h != 256) {
145 throw new IllegalArgumentException("b=1 -> h=256");
146 }
147 if (h == 256 && b == 5) {
148 throw new IllegalArgumentException("h=256 -> b!=5");
149 }
150 this.b = b;
151 this.h = h;
152 this.s = s;
153 this.d = d;
154 this.l = 256 - h;
155 if (h == 1) {
156 cardinality = b * 255 + 1;
157 } else {
158 cardinality = (long) ((long) (l * (1 - Math.pow(h, b)) / (1 - h)) + Math.pow(h, b));
159 }
160 smallest = calculateSmallest();
161 largest = calculateLargest();
162
163 powers = new long[b];
164 Arrays.setAll(powers, c -> (long) Math.pow(h, c));
165 }
166
167 private long calculateLargest() {
168 long result;
169
170 if (d == 1) {
171 return new BHSDCodec(b, h).largest();
172 }
173 switch (s) {
174 case 0:
175 result = cardinality() - 1;
176 break;
177 case 1:
178 result = cardinality() / 2 - 1;
179 break;
180 case 2:
181 result = 3L * cardinality() / 4 - 1;
182 break;
183 default:
184 throw new Error("Unknown s value");
185 }
186 return Math.min((s == 0 ? (long) Integer.MAX_VALUE << 1 : Integer.MAX_VALUE) - 1, result);
187 }
188
189 private long calculateSmallest() {
190 long result;
191 if (d == 1 || !isSigned()) {
192 if (cardinality >= 4294967296L) {
193 result = Integer.MIN_VALUE;
194 } else {
195 result = 0;
196 }
197 } else {
198 result = Math.max(Integer.MIN_VALUE, -cardinality() / (1 << s));
199 }
200 return result;
201 }
202
203
204
205
206
207
208 public long cardinality() {
209 return cardinality;
210 }
211
212 @Override
213 public int decode(final InputStream in) throws IOException, Pack200Exception {
214 if (d != 0) {
215 throw new Pack200Exception("Delta encoding used without passing in last value; this is a coding error");
216 }
217 return decode(in, 0);
218 }
219
220 @Override
221 public int decode(final InputStream in, final long last) throws IOException, Pack200Exception {
222 int n = 0;
223 long z = 0;
224 long x = 0;
225
226 do {
227 x = in.read();
228 lastBandLength++;
229 z += x * powers[n];
230 n++;
231 } while (x >= l && n < b);
232
233 if (x == -1) {
234 throw new EOFException("End of stream reached whilst decoding");
235 }
236
237 if (isSigned()) {
238 final int u = (1 << s) - 1;
239 if ((z & u) == u) {
240 z = z >>> s ^ -1L;
241 } else {
242 z -= z >>> s;
243 }
244 }
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261 if (isDelta()) {
262 z += last;
263 }
264 return (int) z;
265 }
266
267
268
269
270
271
272
273 @Override
274 public int[] decodeInts(final int n, final InputStream in) throws IOException, Pack200Exception {
275 final int[] band = super.decodeInts(n, in);
276 if (isDelta()) {
277 for (int i = 0; i < band.length; i++) {
278 while (band[i] > largest) {
279 band[i] -= cardinality;
280 }
281 while (band[i] < smallest) {
282 band[i] = ExactMath.add(band[i], cardinality);
283 }
284 }
285 }
286 return band;
287 }
288
289 @Override
290 public int[] decodeInts(final int n, final InputStream in, final int firstValue) throws IOException, Pack200Exception {
291 final int[] band = super.decodeInts(n, in, firstValue);
292 if (isDelta()) {
293 for (int i = 0; i < band.length; i++) {
294 while (band[i] > largest) {
295 band[i] -= cardinality;
296 }
297 while (band[i] < smallest) {
298 band[i] = ExactMath.add(band[i], cardinality);
299 }
300 }
301 }
302 return band;
303 }
304
305 @Override
306 public byte[] encode(final int value) throws Pack200Exception {
307 return encode(value, 0);
308 }
309
310 @Override
311 public byte[] encode(final int value, final int last) throws Pack200Exception {
312 if (!encodes(value)) {
313 throw new Pack200Exception("The codec " + this + " does not encode the value " + value);
314 }
315
316 long z = value;
317 if (isDelta()) {
318 z -= last;
319 }
320 if (isSigned()) {
321 if (z < Integer.MIN_VALUE) {
322 z += 4294967296L;
323 } else if (z > Integer.MAX_VALUE) {
324 z -= 4294967296L;
325 }
326 if (z < 0) {
327 z = (-z << s) - 1;
328 } else if (s == 1) {
329 z = z << s;
330 } else {
331 z += (z - z % 3) / 3;
332 }
333 } else if (z < 0) {
334
335
336 z += Math.min(cardinality, 4294967296L);
337 }
338 if (z < 0) {
339 throw new Pack200Exception("unable to encode");
340 }
341
342 final List<Byte> byteList = new ArrayList<>();
343 for (int n = 0; n < b; n++) {
344 long byteN;
345 if (z < l) {
346 byteN = z;
347 } else {
348 byteN = z % h;
349 while (byteN < l) {
350 byteN += h;
351 }
352 }
353 byteList.add(Byte.valueOf((byte) byteN));
354 if (byteN < l) {
355 break;
356 }
357 z -= byteN;
358 z /= h;
359 }
360 final byte[] bytes = new byte[byteList.size()];
361 for (int i = 0; i < bytes.length; i++) {
362 bytes[i] = byteList.get(i).byteValue();
363 }
364 return bytes;
365 }
366
367
368
369
370
371
372
373 public boolean encodes(final long value) {
374 return value >= smallest && value <= largest;
375 }
376
377 @Override
378 public boolean equals(final Object o) {
379 if (o instanceof BHSDCodec) {
380 final BHSDCodec codec = (BHSDCodec) o;
381 return codec.b == b && codec.h == h && codec.s == s && codec.d == d;
382 }
383 return false;
384 }
385
386
387
388
389
390
391 public int getB() {
392 return b;
393 }
394
395
396
397
398
399
400 public int getH() {
401 return h;
402 }
403
404
405
406
407
408
409 public int getL() {
410 return l;
411 }
412
413
414
415
416
417
418 public int getS() {
419 return s;
420 }
421
422 @Override
423 public int hashCode() {
424 return ((b * 37 + h) * 37 + s) * 37 + d;
425 }
426
427
428
429
430
431
432 public boolean isDelta() {
433 return d != 0;
434 }
435
436
437
438
439
440
441 public boolean isSigned() {
442 return s != 0;
443 }
444
445
446
447
448
449
450 public long largest() {
451 return largest;
452 }
453
454
455
456
457
458
459 public long smallest() {
460 return smallest;
461 }
462
463
464
465
466 @Override
467 public String toString() {
468 final StringBuilder buffer = new StringBuilder(11);
469 buffer.append('(');
470 buffer.append(b);
471 buffer.append(',');
472 buffer.append(h);
473 if (s != 0 || d != 0) {
474 buffer.append(',');
475 buffer.append(s);
476 }
477 if (d != 0) {
478 buffer.append(',');
479 buffer.append(d);
480 }
481 buffer.append(')');
482 return buffer.toString();
483 }
484 }