1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * https://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.commons.compress.harmony.unpack200;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.util.Arrays;
24
25 import org.apache.commons.compress.harmony.pack200.BHSDCodec;
26 import org.apache.commons.compress.harmony.pack200.Codec;
27 import org.apache.commons.compress.harmony.pack200.CodecEncoding;
28 import org.apache.commons.compress.harmony.pack200.Pack200Exception;
29 import org.apache.commons.compress.harmony.pack200.PopulationCodec;
30 import org.apache.commons.compress.harmony.unpack200.bytecode.CPClass;
31 import org.apache.commons.compress.harmony.unpack200.bytecode.CPDouble;
32 import org.apache.commons.compress.harmony.unpack200.bytecode.CPFieldRef;
33 import org.apache.commons.compress.harmony.unpack200.bytecode.CPFloat;
34 import org.apache.commons.compress.harmony.unpack200.bytecode.CPInteger;
35 import org.apache.commons.compress.harmony.unpack200.bytecode.CPInterfaceMethodRef;
36 import org.apache.commons.compress.harmony.unpack200.bytecode.CPLong;
37 import org.apache.commons.compress.harmony.unpack200.bytecode.CPMethodRef;
38 import org.apache.commons.compress.harmony.unpack200.bytecode.CPNameAndType;
39 import org.apache.commons.compress.harmony.unpack200.bytecode.CPString;
40 import org.apache.commons.compress.harmony.unpack200.bytecode.CPUTF8;
41 import org.apache.commons.compress.utils.ExactMath;
42 import org.apache.commons.lang3.ArrayUtils;
43
44 /**
45 * Abstract superclass for a set of bands.
46 */
47 public abstract class BandSet {
48
49 /**
50 * Segment.
51 */
52 protected Segment segment;
53
54 /**
55 * Segment header.
56 */
57 protected SegmentHeader header;
58
59 /**
60 * Constructs a new instance for the given segment.
61 *
62 * @param segment The segment.
63 */
64 public BandSet(final Segment segment) {
65 this.segment = segment;
66 this.header = segment.getSegmentHeader();
67 }
68
69 /**
70 * Decodes a band and return an array of {@code int} values.
71 *
72 * @param name the name of the band (for logging and debugging).
73 * @param in the InputStream to decode.
74 * @param codec the default Codec for this band.
75 * @param count the number of elements to read.
76 * @return an array of decoded {@code int} values.
77 * @throws IOException if there is a problem reading from the underlying input stream.
78 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid.
79 */
80 public int[] decodeBandInt(final String name, final InputStream in, final BHSDCodec codec, final int count) throws IOException, Pack200Exception {
81 if (count < 0) {
82 throw new Pack200Exception("count < 0");
83 }
84 // Useful for debugging
85 // if (count > 0) {
86 // System.out.println("decoding " + name + " " + count);
87 // }
88 Codec codecUsed = codec;
89 if (codec.getB() == 1 || count == 0) {
90 return codec.decodeInts(count, in);
91 }
92 final int[] getFirst = codec.decodeInts(1, in);
93 if (getFirst.length == 0) {
94 return getFirst;
95 }
96 final int first = getFirst[0];
97 final int[] band;
98 if (codec.isSigned() && first >= -256 && first <= -1) {
99 // Non-default codec should be used
100 codecUsed = CodecEncoding.getCodec(-1 - first, header.getBandHeadersInputStream(), codec);
101 band = codecUsed.decodeInts(count, in);
102 } else if (!codec.isSigned() && first >= codec.getL() && first <= codec.getL() + 255) {
103 // Non-default codec should be used
104 codecUsed = CodecEncoding.getCodec(first - codec.getL(), header.getBandHeadersInputStream(), codec);
105 band = codecUsed.decodeInts(count, in);
106 } else {
107 // First element should not be discarded
108 band = codec.decodeInts(count - 1, in, first);
109 }
110 // Useful for debugging -E options:
111 // if (!codecUsed.equals(codec)) {
112 // int bytes = codecUsed.lastBandLength;
113 // System.out.println(count + " " + name + " encoded with " + codecUsed + " " + bytes);
114 // }
115 if (codecUsed instanceof PopulationCodec) {
116 final PopulationCodec popCodec = (PopulationCodec) codecUsed;
117 final int[] favoured = popCodec.getFavoured().clone();
118 Arrays.sort(favoured);
119 for (int i = 0; i < band.length; i++) {
120 final boolean favouredValue = Arrays.binarySearch(favoured, band[i]) > -1;
121 final Codec theCodec = favouredValue ? popCodec.getFavouredCodec() : popCodec.getUnfavouredCodec();
122 if (theCodec instanceof BHSDCodec && ((BHSDCodec) theCodec).isDelta()) {
123 final BHSDCodec bhsd = (BHSDCodec) theCodec;
124 final long cardinality = bhsd.cardinality();
125 while (band[i] > bhsd.largest()) {
126 band[i] -= cardinality;
127 }
128 while (band[i] < bhsd.smallest()) {
129 band[i] = ExactMath.add(band[i], cardinality);
130 }
131 }
132 }
133 }
134 return band;
135 }
136
137 /**
138 * Decodes a band and return an array of {@code int[]} values.
139 *
140 * @param name the name of the band (primarily for logging/debugging purposes)
141 * @param in the InputStream to decode from
142 * @param defaultCodec the default codec for this band
143 * @param counts the numbers of elements to read for each int array within the array to be returned
144 * @return an array of decoded {@code int[]} values
145 * @throws IOException if there is a problem reading from the underlying input stream
146 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid
147 */
148 public int[][] decodeBandInt(final String name, final InputStream in, final BHSDCodec defaultCodec, final int[] counts)
149 throws IOException, Pack200Exception {
150 final int[][] result = new int[counts.length][];
151 int totalCount = 0;
152 for (final int count : counts) {
153 totalCount += count;
154 }
155 final int[] twoDResult = decodeBandInt(name, in, defaultCodec, totalCount);
156 int index = 0;
157 for (int i = 0; i < result.length; i++) {
158 if (counts[i] > twoDResult.length) {
159 throw new IOException("Counts value exceeds length of twoDResult");
160 }
161 result[i] = new int[counts[i]];
162 for (int j = 0; j < result[i].length; j++) {
163 result[i][j] = twoDResult[index];
164 index++;
165 }
166 }
167 return result;
168 }
169
170 /**
171 * Gets a new array of String references for the subset defined by the given {@code ints} array.
172 *
173 * @param ints The indices into the {@code reference} array.
174 * @param reference The source array.
175 * @return a new array.
176 */
177 protected String[] getReferences(final int[] ints, final String[] reference) {
178 return ArrayUtils.setAll(new String[ints.length], i -> reference[ints[i]]);
179 }
180
181 /**
182 * Gets a new array of String references for the subset defined by the given {@code ints} array.
183 *
184 * @param ints The indices into the {@code reference} array.
185 * @param reference The source array.
186 * @return a new array.
187 */
188 protected String[][] getReferences(final int[][] ints, final String[] reference) {
189 final String[][] result = new String[ints.length][];
190 for (int i = 0; i < result.length; i++) {
191 result[i] = new String[ints[i].length];
192 for (int j = 0; j < result[i].length; j++) {
193 result[i][j] = reference[ints[i][j]];
194 }
195 }
196 return result;
197 }
198
199 /**
200 * Parses an input stream into an array of {@link CPClass}.
201 *
202 * @param name the name of the band (for logging and debugging).
203 * @param in the input stream to parse.
204 * @param codec the default {@link BHSDCodec} for this band
205 * @param count the number of elements to read.
206 * @return an array of decoded {@link CPClass}.
207 * @throws IOException if there is a problem reading from the underlying input stream.
208 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid.
209 */
210 public CPClass[] parseCPClassReferences(final String name, final InputStream in, final BHSDCodec codec, final int count)
211 throws IOException, Pack200Exception {
212 final int[] indices = decodeBandInt(name, in, codec, count);
213 final CpBands cpBands = segment.getCpBands();
214 return ArrayUtils.setAll(new CPClass[indices.length], i -> cpBands.cpClassValue(indices[i]));
215 }
216
217 /**
218 * Parses an input stream into an array of {@link CPNameAndType}.
219 *
220 * @param name the name of the band (for logging and debugging).
221 * @param in the input stream to parse.
222 * @param codec the default {@link BHSDCodec} for this band
223 * @param count the number of elements to read.
224 * @return an array of decoded {@link CPNameAndType}.
225 * @throws IOException if there is a problem reading from the underlying input stream.
226 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid.
227 */
228 public CPNameAndType[] parseCPDescriptorReferences(final String name, final InputStream in, final BHSDCodec codec, final int count)
229 throws IOException, Pack200Exception {
230 final int[] indices = decodeBandInt(name, in, codec, count);
231 final CpBands cpBands = segment.getCpBands();
232 return ArrayUtils.setAll(new CPNameAndType[indices.length], i -> cpBands.cpNameAndTypeValue(indices[i]));
233 }
234
235 /**
236 * Parses an input stream into an array of {@link CPDouble}.
237 *
238 * @param name the name of the band (for logging and debugging).
239 * @param in the input stream to parse.
240 * @param codec the default {@link BHSDCodec} for this band
241 * @param count the number of elements to read.
242 * @return an array of decoded {@link CPDouble}.
243 * @throws IOException if there is a problem reading from the underlying input stream.
244 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid.
245 */
246 public CPDouble[] parseCPDoubleReferences(final String name, final InputStream in, final BHSDCodec codec, final int count)
247 throws IOException, Pack200Exception {
248 final int[] indices = decodeBandInt(name, in, codec, count);
249 final CpBands cpBands = segment.getCpBands();
250 return ArrayUtils.setAll(new CPDouble[indices.length], i -> cpBands.cpDoubleValue(indices[i]));
251 }
252
253 /**
254 * Parses an input stream into an array of {@link CPFieldRef}.
255 *
256 * @param name the name of the band (for logging and debugging).
257 * @param in the input stream to parse.
258 * @param codec the default {@link BHSDCodec} for this band
259 * @param count the number of elements to read.
260 * @return an array of decoded {@link CPFieldRef}.
261 * @throws IOException if there is a problem reading from the underlying input stream.
262 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid.
263 */
264 public CPFieldRef[] parseCPFieldRefReferences(final String name, final InputStream in, final BHSDCodec codec, final int count)
265 throws IOException, Pack200Exception {
266 final int[] indices = decodeBandInt(name, in, codec, count);
267 final CpBands cpBands = segment.getCpBands();
268 return ArrayUtils.setAll(new CPFieldRef[indices.length], i -> cpBands.cpFieldValue(indices[i]));
269 }
270
271 /**
272 * Parses an input stream into an array of {@link CPFloat}.
273 *
274 * @param name the name of the band (for logging and debugging).
275 * @param in the input stream to parse.
276 * @param codec the default {@link BHSDCodec} for this band
277 * @param count the number of elements to read.
278 * @return an array of decoded {@link CPFloat}.
279 * @throws IOException if there is a problem reading from the underlying input stream.
280 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid.
281 */
282 public CPFloat[] parseCPFloatReferences(final String name, final InputStream in, final BHSDCodec codec, final int count)
283 throws IOException, Pack200Exception {
284 final int[] indices = decodeBandInt(name, in, codec, count);
285 final CpBands cpBands = segment.getCpBands();
286 return ArrayUtils.setAll(new CPFloat[indices.length], i -> cpBands.cpFloatValue(indices[i]));
287 }
288
289 /**
290 * Parses an input stream into an array of {@link CPInterfaceMethodRef}.
291 *
292 * @param name the name of the band (for logging and debugging).
293 * @param in the input stream to parse.
294 * @param codec the default {@link BHSDCodec} for this band
295 * @param count the number of elements to read.
296 * @return an array of decoded {@link CPInterfaceMethodRef}.
297 * @throws IOException if there is a problem reading from the underlying input stream.
298 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid.
299 */
300 public CPInterfaceMethodRef[] parseCPInterfaceMethodRefReferences(final String name, final InputStream in, final BHSDCodec codec, final int count)
301 throws IOException, Pack200Exception {
302 final int[] indices = decodeBandInt(name, in, codec, count);
303 final CpBands cpBands = segment.getCpBands();
304 return ArrayUtils.setAll(new CPInterfaceMethodRef[indices.length], i -> cpBands.cpIMethodValue(indices[i]));
305 }
306
307 /**
308 * Parses an input stream into an array of {@link CPInteger}.
309 *
310 * @param name the name of the band (for logging and debugging).
311 * @param in the input stream to parse.
312 * @param codec the default {@link BHSDCodec} for this band
313 * @param count the number of elements to read.
314 * @return an array of decoded {@link CPInteger}.
315 * @throws IOException if there is a problem reading from the underlying input stream.
316 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid.
317 */
318 public CPInteger[] parseCPIntReferences(final String name, final InputStream in, final BHSDCodec codec, final int count)
319 throws IOException, Pack200Exception {
320 final CpBands cpBands = segment.getCpBands();
321 final int[] reference = cpBands.getCpInt();
322 final int[] indices = decodeBandInt(name, in, codec, count);
323 final CPInteger[] result = new CPInteger[indices.length];
324 for (int i = 0; i < count; i++) {
325 final int index = indices[i];
326 if (index < 0 || index >= reference.length) {
327 throw new Pack200Exception("Something has gone wrong during parsing references, index = " + index + ", array size = " + reference.length);
328 }
329 result[i] = cpBands.cpIntegerValue(index);
330 }
331 return result;
332 }
333
334 /**
335 * Parses an input stream into an array of {@link CPLong}.
336 *
337 * @param name the name of the band (for logging and debugging).
338 * @param in the input stream to parse.
339 * @param codec the default {@link BHSDCodec} for this band
340 * @param count the number of elements to read.
341 * @return an array of decoded {@link CPLong}.
342 * @throws IOException if there is a problem reading from the underlying input stream.
343 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid.
344 */
345 public CPLong[] parseCPLongReferences(final String name, final InputStream in, final BHSDCodec codec, final int count)
346 throws IOException, Pack200Exception {
347 final CpBands cpBands = segment.getCpBands();
348 final long[] reference = cpBands.getCpLong();
349 final int[] indices = decodeBandInt(name, in, codec, count);
350 final CPLong[] result = new CPLong[indices.length];
351 for (int i = 0; i < count; i++) {
352 final int index = indices[i];
353 if (index < 0 || index >= reference.length) {
354 throw new Pack200Exception("Something has gone wrong during parsing references, index = " + index + ", array size = " + reference.length);
355 }
356 result[i] = cpBands.cpLongValue(index);
357 }
358 return result;
359 }
360
361 /**
362 * Parses an input stream into an array of {@link CPMethodRef}.
363 *
364 * @param name the name of the band (for logging and debugging).
365 * @param in the input stream to parse.
366 * @param codec the default {@link BHSDCodec} for this band
367 * @param count the number of elements to read.
368 * @return an array of decoded {@link CPMethodRef}.
369 * @throws IOException if there is a problem reading from the underlying input stream.
370 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid.
371 */
372 public CPMethodRef[] parseCPMethodRefReferences(final String name, final InputStream in, final BHSDCodec codec, final int count)
373 throws IOException, Pack200Exception {
374 final int[] indices = decodeBandInt(name, in, codec, count);
375 final CpBands cpBands = segment.getCpBands();
376 return ArrayUtils.setAll(new CPMethodRef[indices.length], i -> cpBands.cpMethodValue(indices[i]));
377 }
378
379 /**
380 * Parses an input stream into an array of {@link CPUTF8}.
381 *
382 * @param name the name of the band (for logging and debugging).
383 * @param in the input stream to parse.
384 * @param codec the default {@link BHSDCodec} for this band
385 * @param count the number of elements to read.
386 * @return an array of decoded {@link CPUTF8}.
387 * @throws IOException if there is a problem reading from the underlying input stream.
388 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid.
389 */
390 public CPUTF8[] parseCPSignatureReferences(final String name, final InputStream in, final BHSDCodec codec, final int count)
391 throws IOException, Pack200Exception {
392 final int[] indices = decodeBandInt(name, in, codec, count);
393 final CpBands cpBands = segment.getCpBands();
394 return ArrayUtils.setAll(new CPUTF8[indices.length], i -> cpBands.cpSignatureValue(indices[i]));
395 }
396
397 /**
398 * Parses an input stream into an array of array of {@link CPUTF8}.
399 *
400 * @param name the name of the band (for logging and debugging).
401 * @param in the input stream to parse.
402 * @param codec the default {@link BHSDCodec} for this band
403 * @param counts the number of elements to read.
404 * @return an array of array of decoded {@link CPUTF8}.
405 * @throws IOException if there is a problem reading from the underlying input stream.
406 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid.
407 */
408 protected CPUTF8[][] parseCPSignatureReferences(final String name, final InputStream in, final BHSDCodec codec, final int[] counts)
409 throws IOException, Pack200Exception {
410 int sum = 0;
411 for (final int count : counts) {
412 sum += count;
413 }
414 final int[] indices = decodeBandInt(name, in, codec, sum);
415 final CpBands cpBands = segment.getCpBands();
416 final CPUTF8[] result1 = ArrayUtils.setAll(new CPUTF8[sum], i -> cpBands.cpSignatureValue(indices[i]));
417 int pos = 0;
418 final CPUTF8[][] result = new CPUTF8[counts.length][];
419 for (int i = 0; i < counts.length; i++) {
420 final int num = counts[i];
421 result[i] = new CPUTF8[num];
422 System.arraycopy(result1, pos, result[i], 0, num);
423 pos += num;
424 }
425 return result;
426 }
427
428 /**
429 * Parses an input stream into an array of {@link CPString}.
430 *
431 * @param name the name of the band (for logging and debugging).
432 * @param in the input stream to parse.
433 * @param codec the default {@link BHSDCodec} for this band
434 * @param count the number of elements to read.
435 * @return an array of decoded {@link CPString}.
436 * @throws IOException if there is a problem reading from the underlying input stream.
437 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid.
438 */
439 public CPString[] parseCPStringReferences(final String name, final InputStream in, final BHSDCodec codec, final int count)
440 throws IOException, Pack200Exception {
441 final int[] indices = decodeBandInt(name, in, codec, count);
442 final CpBands cpBands = segment.getCpBands();
443 return ArrayUtils.setAll(new CPString[indices.length], i -> cpBands.cpStringValue(indices[i]));
444 }
445
446 /**
447 * Parses an input stream into an array of {@link CPUTF8}.
448 *
449 * @param name the name of the band (for logging and debugging).
450 * @param in the input stream to parse.
451 * @param codec the default {@link BHSDCodec} for this band
452 * @param count the number of elements to read.
453 * @return an array of decoded {@link CPUTF8}.
454 * @throws IOException if there is a problem reading from the underlying input stream.
455 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid.
456 */
457 public CPUTF8[] parseCPUTF8References(final String name, final InputStream in, final BHSDCodec codec, final int count)
458 throws IOException, Pack200Exception {
459 final int[] indices = decodeBandInt(name, in, codec, count);
460 final CpBands cpBands = segment.getCpBands();
461 return ArrayUtils.setAll(new CPUTF8[indices.length], i -> cpBands.cpUTF8Value(indices[i]));
462 }
463
464 /**
465 * Parses an input stream into an array of array of {@link CPUTF8}.
466 *
467 * @param name the name of the band (for logging and debugging).
468 * @param in the input stream to parse.
469 * @param codec the default {@link BHSDCodec} for this band
470 * @param counts the number of elements to read.
471 * @return an array of array of decoded {@link CPUTF8}.
472 * @throws IOException if there is a problem reading from the underlying input stream.
473 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid.
474 */
475 public CPUTF8[][] parseCPUTF8References(final String name, final InputStream in, final BHSDCodec codec, final int[] counts)
476 throws IOException, Pack200Exception {
477 final CPUTF8[][] result = new CPUTF8[counts.length][];
478 int sum = 0;
479 for (int i = 0; i < counts.length; i++) {
480 result[i] = new CPUTF8[counts[i]];
481 sum += counts[i];
482 }
483 final int[] indices = decodeBandInt(name, in, codec, sum);
484 final CpBands cpBands = segment.getCpBands();
485 final CPUTF8[] result1 = ArrayUtils.setAll(new CPUTF8[sum], i -> cpBands.cpUTF8Value(indices[i]));
486 int pos = 0;
487 for (int i = 0; i < counts.length; i++) {
488 final int num = counts[i];
489 result[i] = new CPUTF8[num];
490 System.arraycopy(result1, pos, result[i], 0, num);
491 pos += num;
492 }
493 return result;
494 }
495
496 /**
497 * Parses an input stream into an array of flags.
498 *
499 * @param name the name of the band (for logging and debugging).
500 * @param in the InputStream to decode.
501 * @param count the number of elements to read.
502 * @param hiCodec an optional codec if the high flag is on.
503 * @param loCodec the codec.
504 * @return an array of decoded {@code long} flags.
505 * @throws IOException if there is a problem reading from the underlying input stream.
506 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid.
507 */
508 public long[] parseFlags(final String name, final InputStream in, final int count, final BHSDCodec hiCodec, final BHSDCodec loCodec)
509 throws IOException, Pack200Exception {
510 return parseFlags(name, in, new int[] { count }, hiCodec, loCodec)[0];
511 }
512
513 /**
514 * Parses an input stream into an array of flags.
515 *
516 * @param name the name of the band (for logging and debugging).
517 * @param in the InputStream to decode.
518 * @param count the number of elements to read.
519 * @param codec the codec.
520 * @param hasHi whether to the high flag is enabled.
521 * @return an array of decoded {@code long} flags.
522 * @throws IOException if there is a problem reading from the underlying input stream.
523 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid.
524 */
525 public long[] parseFlags(final String name, final InputStream in, final int count, final BHSDCodec codec, final boolean hasHi)
526 throws IOException, Pack200Exception {
527 return parseFlags(name, in, new int[] { count }, hasHi ? codec : null, codec)[0];
528 }
529
530 /**
531 * Parses an input stream into an array of flags.
532 *
533 * @param name the name of the band (for logging and debugging).
534 * @param in the InputStream to decode.
535 * @param counts the number of elements to read.
536 * @param hiCodec an optional codec if the high flag is on.
537 * @param loCodec the codec.
538 * @return an array of decoded {@code long} flags.
539 * @throws IOException if there is a problem reading from the underlying input stream.
540 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid.
541 */
542 public long[][] parseFlags(final String name, final InputStream in, final int[] counts, final BHSDCodec hiCodec, final BHSDCodec loCodec)
543 throws IOException, Pack200Exception {
544 final int count = counts.length;
545 if (count == 0) {
546 return new long[][] { {} };
547 }
548 int sum = 0;
549 final long[][] result = new long[count][];
550 for (int i = 0; i < count; i++) {
551 sum += counts[i];
552 }
553 int[] hi = null;
554 final int[] lo;
555 if (hiCodec != null) {
556 hi = decodeBandInt(name, in, hiCodec, sum);
557 }
558 lo = decodeBandInt(name, in, loCodec, sum);
559
560 int index = 0;
561 for (int i = 0; i < count; i++) {
562 result[i] = new long[counts[i]];
563 for (int j = 0; j < result[i].length; j++) {
564 if (hi != null) {
565 result[i][j] = (long) hi[index] << 32 | lo[index] & 4294967295L;
566 } else {
567 result[i][j] = lo[index];
568 }
569 index++;
570 }
571 }
572 return result;
573 }
574
575 /**
576 * Parses an input stream into an array of flags.
577 *
578 * @param name the name of the band (for logging and debugging).
579 * @param in the InputStream to decode.
580 * @param counts the number of elements to read.
581 * @param codec the codec.
582 * @param hasHi whether the high flag is enabnled.
583 * @return an array of decoded {@code long} flags.
584 * @throws IOException if there is a problem reading from the underlying input stream.
585 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid.
586 */
587 public long[][] parseFlags(final String name, final InputStream in, final int[] counts, final BHSDCodec codec, final boolean hasHi)
588 throws IOException, Pack200Exception {
589 return parseFlags(name, in, counts, hasHi ? codec : null, codec);
590 }
591
592 /**
593 * Parses <em>count</em> references from {@code in}, using {@code codec} to decode the values as indexes into {@code reference} (which is populated prior to
594 * this call). An exception is thrown if a decoded index falls outside the range [0..reference.length-1].
595 *
596 * @param name the band name
597 * @param in the input stream to read from
598 * @param codec the BHSDCodec to use for decoding
599 * @param count the number of references to decode
600 * @param reference the array of values to use for the references
601 * @return Parsed references.
602 * @throws IOException if a problem occurs during reading from the underlying stream.
603 * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported Codec.
604 */
605 public String[] parseReferences(final String name, final InputStream in, final BHSDCodec codec, final int count, final String[] reference)
606 throws IOException, Pack200Exception {
607 return parseReferences(name, in, codec, new int[] { count }, reference)[0];
608 }
609
610 /**
611 * Parses <em>count</em> references from {@code in}, using {@code codec} to decode the values as indexes into {@code reference} (which is populated prior to
612 * this call). An exception is thrown if a decoded index falls outside the range [0..reference.length-1]. Unlike the other parseReferences, this
613 * post-processes the result into an array of results.
614 *
615 * @param name the name of the band (for logging and debugging).
616 * @param in the input stream to read from.
617 * @param codec the BHSDCodec to use for decoding.
618 * @param counts the numbers of references to decode for each array entry.
619 * @param reference the array of values to use for the references.
620 * @return Parsed references.
621 * @throws IOException if a problem occurs during reading from the underlying stream.
622 * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported Codec.
623 */
624 public String[][] parseReferences(final String name, final InputStream in, final BHSDCodec codec, final int[] counts, final String[] reference)
625 throws IOException, Pack200Exception {
626 final int count = counts.length;
627 if (count == 0) {
628 return new String[][] { {} };
629 }
630 int sum = 0;
631 for (int i = 0; i < count; i++) {
632 sum += counts[i];
633 }
634 // TODO Merge the decode and parsing of a multiple structure into one
635 final int[] indices = decodeBandInt(name, in, codec, sum);
636 final String[] result1 = new String[sum];
637 for (int i1 = 0; i1 < sum; i1++) {
638 final int index = indices[i1];
639 if (index < 0 || index >= reference.length) {
640 throw new Pack200Exception("Something has gone wrong during parsing references, index = " + index + ", array size = " + reference.length);
641 }
642 result1[i1] = reference[index];
643 }
644 // TODO Merge the decode and parsing of a multiple structure into one
645 final String[][] result = new String[count][];
646 int pos = 0;
647 for (int i = 0; i < count; i++) {
648 final int num = counts[i];
649 result[i] = new String[num];
650 System.arraycopy(result1, pos, result[i], 0, num);
651 pos += num;
652 }
653 return result;
654 }
655
656 /**
657 * Reads the input stream.
658 *
659 * @param inputStream the stream to read.
660 * @throws IOException if a problem occurs during reading from the underlying stream.
661 * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported Codec.
662 */
663 public abstract void read(InputStream inputStream) throws IOException, Pack200Exception;
664
665 /**
666 * Unpacks this instance.
667 *
668 * @throws IOException if a problem occurs during reading from the underlying stream.
669 * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported Codec.
670 */
671 public abstract void unpack() throws IOException, Pack200Exception;
672
673 /**
674 * Unpacks the input stream.
675 *
676 * @param in the stream to unpack.
677 * @throws IOException if a problem occurs during reading from the underlying stream.
678 * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported Codec.
679 */
680 public void unpack(final InputStream in) throws IOException, Pack200Exception {
681 read(in);
682 unpack();
683 }
684
685 }