    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}