001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.commons.compress.harmony.unpack200;
020
021import java.io.IOException;
022import java.io.InputStream;
023import java.util.Arrays;
024
025import org.apache.commons.compress.harmony.pack200.BHSDCodec;
026import org.apache.commons.compress.harmony.pack200.Codec;
027import org.apache.commons.compress.harmony.pack200.CodecEncoding;
028import org.apache.commons.compress.harmony.pack200.Pack200Exception;
029import org.apache.commons.compress.harmony.pack200.PopulationCodec;
030import org.apache.commons.compress.harmony.unpack200.bytecode.CPClass;
031import org.apache.commons.compress.harmony.unpack200.bytecode.CPDouble;
032import org.apache.commons.compress.harmony.unpack200.bytecode.CPFieldRef;
033import org.apache.commons.compress.harmony.unpack200.bytecode.CPFloat;
034import org.apache.commons.compress.harmony.unpack200.bytecode.CPInteger;
035import org.apache.commons.compress.harmony.unpack200.bytecode.CPInterfaceMethodRef;
036import org.apache.commons.compress.harmony.unpack200.bytecode.CPLong;
037import org.apache.commons.compress.harmony.unpack200.bytecode.CPMethodRef;
038import org.apache.commons.compress.harmony.unpack200.bytecode.CPNameAndType;
039import org.apache.commons.compress.harmony.unpack200.bytecode.CPString;
040import org.apache.commons.compress.harmony.unpack200.bytecode.CPUTF8;
041import org.apache.commons.compress.utils.ExactMath;
042import org.apache.commons.lang3.ArrayUtils;
043
044/**
045 * Abstract superclass for a set of bands.
046 */
047public abstract class BandSet {
048
049    /**
050     * Segment.
051     */
052    protected Segment segment;
053
054    /**
055     * Segment header.
056     */
057    protected SegmentHeader header;
058
059    /**
060     * Constructs a new instance for the given segment.
061     *
062     * @param segment The segment.
063     */
064    public BandSet(final Segment segment) {
065        this.segment = segment;
066        this.header = segment.getSegmentHeader();
067    }
068
069    /**
070     * Decodes a band and return an array of {@code int} values.
071     *
072     * @param name  the name of the band (for logging and debugging).
073     * @param in    the InputStream to decode.
074     * @param codec the default Codec for this band.
075     * @param count the number of elements to read.
076     * @return an array of decoded {@code int} values.
077     * @throws IOException      if there is a problem reading from the underlying input stream.
078     * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid.
079     */
080    public int[] decodeBandInt(final String name, final InputStream in, final BHSDCodec codec, final int count) throws IOException, Pack200Exception {
081        if (count < 0) {
082            throw new Pack200Exception("count < 0");
083        }
084        // Useful for debugging
085//        if (count > 0) {
086//            System.out.println("decoding " + name + " " + count);
087//        }
088        Codec codecUsed = codec;
089        if (codec.getB() == 1 || count == 0) {
090            return codec.decodeInts(count, in);
091        }
092        final int[] getFirst = codec.decodeInts(1, in);
093        if (getFirst.length == 0) {
094            return getFirst;
095        }
096        final int first = getFirst[0];
097        final int[] band;
098        if (codec.isSigned() && first >= -256 && first <= -1) {
099            // 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}