View Javadoc
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 }