    001/*
    002 *  Licensed to the Apache Software Foundation (ASF) under one or more
    003 *  contributor license agreements.  See the NOTICE file distributed with
    004 *  this work for additional information regarding copyright ownership.
    005 *  The ASF licenses this file to You under the Apache License, Version 2.0
    006 *  (the "License"); you may not use this file except in compliance with
    007 *  the License.  You may obtain a copy of the License at
    008 *
    009 *     http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 *  Unless required by applicable law or agreed to in writing, software
    012 *  distributed under the License is distributed on an "AS IS" BASIS,
    013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 *  See the License for the specific language governing permissions and
    015 *  limitations under the License.
    016 */
    017package org.apache.commons.compress.harmony.unpack200;
    018
    019import java.io.IOException;
    020import java.io.InputStream;
    021import java.util.Arrays;
    022import java.util.HashMap;
    023import java.util.Map;
    024
    025import org.apache.commons.compress.harmony.pack200.Codec;
    026import org.apache.commons.compress.harmony.pack200.Pack200Exception;
    027import org.apache.commons.compress.harmony.unpack200.bytecode.CPClass;
    028import org.apache.commons.compress.harmony.unpack200.bytecode.CPDouble;
    029import org.apache.commons.compress.harmony.unpack200.bytecode.CPFieldRef;
    030import org.apache.commons.compress.harmony.unpack200.bytecode.CPFloat;
    031import org.apache.commons.compress.harmony.unpack200.bytecode.CPInteger;
    032import org.apache.commons.compress.harmony.unpack200.bytecode.CPInterfaceMethodRef;
    033import org.apache.commons.compress.harmony.unpack200.bytecode.CPLong;
    034import org.apache.commons.compress.harmony.unpack200.bytecode.CPMethodRef;
    035import org.apache.commons.compress.harmony.unpack200.bytecode.CPNameAndType;
    036import org.apache.commons.compress.harmony.unpack200.bytecode.CPString;
    037import org.apache.commons.compress.harmony.unpack200.bytecode.CPUTF8;
    038
    039/**
    040 * Constant Pool bands
    041 */
    042public class CpBands extends BandSet {
    043
    044    private static final String EMPTY_STRING = ""; //$NON-NLS-1$
    045
    046    private final SegmentConstantPool pool = new SegmentConstantPool(this);
    047
    048    private String[] cpClass;
    049
    050    private int[] cpClassInts;
    051    private int[] cpDescriptorNameInts;
    052    private int[] cpDescriptorTypeInts;
    053    private String[] cpDescriptor;
    054    private double[] cpDouble;
    055    private String[] cpFieldClass;
    056    private String[] cpFieldDescriptor;
    057    private int[] cpFieldClassInts;
    058    private int[] cpFieldDescriptorInts;
    059    private float[] cpFloat;
    060    private String[] cpIMethodClass;
    061    private String[] cpIMethodDescriptor;
    062    private int[] cpIMethodClassInts;
    063    private int[] cpIMethodDescriptorInts;
    064    private int[] cpInt;
    065    private long[] cpLong;
    066    private String[] cpMethodClass;
    067    private String[] cpMethodDescriptor;
    068    private int[] cpMethodClassInts;
    069    private int[] cpMethodDescriptorInts;
    070    private String[] cpSignature;
    071    private int[] cpSignatureInts;
    072    private String[] cpString;
    073    private int[] cpStringInts;
    074    private String[] cpUTF8;
    075    private final Map<String, CPUTF8> stringsToCPUTF8 = new HashMap<>();
    076
    077    private final Map<String, CPString> stringsToCPStrings = new HashMap<>();
    078    private final Map<Long, CPLong> longsToCPLongs = new HashMap<>();
    079    private final Map<Integer, CPInteger> integersToCPIntegers = new HashMap<>();
    080    private final Map<Float, CPFloat> floatsToCPFloats = new HashMap<>();
    081    private final Map<String, CPClass> stringsToCPClass = new HashMap<>();
    082    private final Map<Double, CPDouble> doublesToCPDoubles = new HashMap<>();
    083    private final Map<String, CPNameAndType> descriptorsToCPNameAndTypes = new HashMap<>();
    084    private Map<String, Integer> mapClass;
    085
    086    private Map<String, Integer> mapDescriptor;
    087    private Map<String, Integer> mapUTF8;
    088    // TODO: Not used
    089    private Map<String, Integer> mapSignature;
    090
    091    private int intOffset;
    092
    093    private int floatOffset;
    094    private int longOffset;
    095    private int doubleOffset;
    096    private int stringOffset;
    097    private int classOffset;
    098    private int signatureOffset;
    099    private int descrOffset;
    100    private int fieldOffset;
    101    private int methodOffset;
    102    private int imethodOffset;
    103
    104    public CpBands(final Segment segment) {
    105        super(segment);
    106    }
    107
    108    public CPClass cpClassValue(final int index) {
    109        final String string = cpClass[index];
    110        final int utf8Index = cpClassInts[index];
    111        final int globalIndex = classOffset + index;
    112        return stringsToCPClass.computeIfAbsent(string, k -> new CPClass(cpUTF8Value(utf8Index), globalIndex));
    113    }
    114
    115    public CPClass cpClassValue(final String string) {
    116        CPClass cpString = stringsToCPClass.get(string);
    117        if (cpString == null) {
    118            final Integer index = mapClass.get(string);
    119            if (index != null) {
    120                return cpClassValue(index.intValue());
    121            }
    122            cpString = new CPClass(cpUTF8Value(string, false), -1);
    123            stringsToCPClass.put(string, cpString);
    124        }
    125        return cpString;
    126    }
    127
    128    public CPDouble cpDoubleValue(final int index) {
    129        final Double dbl = Double.valueOf(cpDouble[index]);
    130        CPDouble cpDouble = doublesToCPDoubles.get(dbl);
    131        if (cpDouble == null) {
    132            cpDouble = new CPDouble(dbl, index + doubleOffset);
    133            doublesToCPDoubles.put(dbl, cpDouble);
    134        }
    135        return cpDouble;
    136    }
    137
    138    public CPFieldRef cpFieldValue(final int index) {
    139        return new CPFieldRef(cpClassValue(cpFieldClassInts[index]), cpNameAndTypeValue(cpFieldDescriptorInts[index]), index + fieldOffset);
    140    }
    141
    142    public CPFloat cpFloatValue(final int index) {
    143        final Float f = Float.valueOf(cpFloat[index]);
    144        CPFloat cpFloat = floatsToCPFloats.get(f);
    145        if (cpFloat == null) {
    146            cpFloat = new CPFloat(f, index + floatOffset);
    147            floatsToCPFloats.put(f, cpFloat);
    148        }
    149        return cpFloat;
    150    }
    151
    152    public CPInterfaceMethodRef cpIMethodValue(final int index) {
    153        return new CPInterfaceMethodRef(cpClassValue(cpIMethodClassInts[index]), cpNameAndTypeValue(cpIMethodDescriptorInts[index]), index + imethodOffset);
    154    }
    155
    156    public CPInteger cpIntegerValue(final int index) {
    157        final Integer i = Integer.valueOf(cpInt[index]);
    158        CPInteger cpInteger = integersToCPIntegers.get(i);
    159        if (cpInteger == null) {
    160            cpInteger = new CPInteger(i, index + intOffset);
    161            integersToCPIntegers.put(i, cpInteger);
    162        }
    163        return cpInteger;
    164    }
    165
    166    public CPLong cpLongValue(final int index) {
    167        final Long l = Long.valueOf(cpLong[index]);
    168        CPLong cpLong = longsToCPLongs.get(l);
    169        if (cpLong == null) {
    170            cpLong = new CPLong(l, index + longOffset);
    171            longsToCPLongs.put(l, cpLong);
    172        }
    173        return cpLong;
    174    }
    175
    176    public CPMethodRef cpMethodValue(final int index) {
    177        return new CPMethodRef(cpClassValue(cpMethodClassInts[index]), cpNameAndTypeValue(cpMethodDescriptorInts[index]), index + methodOffset);
    178    }
    179
    180    public CPNameAndType cpNameAndTypeValue(final int index) {
    181        final String descriptor = cpDescriptor[index];
    182        CPNameAndType cpNameAndType = descriptorsToCPNameAndTypes.get(descriptor);
    183        if (cpNameAndType == null) {
    184            final int nameIndex = cpDescriptorNameInts[index];
    185            final int descriptorIndex = cpDescriptorTypeInts[index];
    186
    187            final CPUTF8 name = cpUTF8Value(nameIndex);
    188            final CPUTF8 descriptorU = cpSignatureValue(descriptorIndex);
    189            cpNameAndType = new CPNameAndType(name, descriptorU, index + descrOffset);
    190            descriptorsToCPNameAndTypes.put(descriptor, cpNameAndType);
    191        }
    192        return cpNameAndType;
    193    }
    194
    195    public CPNameAndType cpNameAndTypeValue(final String descriptor) {
    196        CPNameAndType cpNameAndType = descriptorsToCPNameAndTypes.get(descriptor);
    197        if (cpNameAndType == null) {
    198            final Integer index = mapDescriptor.get(descriptor);
    199            if (index != null) {
    200                return cpNameAndTypeValue(index.intValue());
    201            }
    202            final int colon = descriptor.indexOf(':');
    203            final String nameString = descriptor.substring(0, colon);
    204            final String descriptorString = descriptor.substring(colon + 1);
    205
    206            final CPUTF8 name = cpUTF8Value(nameString, true);
    207            final CPUTF8 descriptorU = cpUTF8Value(descriptorString, true);
    208            cpNameAndType = new CPNameAndType(name, descriptorU, -1 + descrOffset);
    209            descriptorsToCPNameAndTypes.put(descriptor, cpNameAndType);
    210        }
    211        return cpNameAndType;
    212    }
    213
    214    public CPUTF8 cpSignatureValue(final int index) {
    215        int globalIndex;
    216        if (cpSignatureInts[index] != -1) {
    217            globalIndex = cpSignatureInts[index];
    218        } else {
    219            globalIndex = index + signatureOffset;
    220        }
    221        final String string = cpSignature[index];
    222        CPUTF8 cpUTF8 = stringsToCPUTF8.get(string);
    223        if (cpUTF8 == null) {
    224            cpUTF8 = new CPUTF8(string, globalIndex);
    225            stringsToCPUTF8.put(string, cpUTF8);
    226        }
    227        return cpUTF8;
    228    }
    229
    230    public CPString cpStringValue(final int index) {
    231        final String string = cpString[index];
    232        final int utf8Index = cpStringInts[index];
    233        final int globalIndex = stringOffset + index;
    234        CPString cpString = stringsToCPStrings.get(string);
    235        if (cpString == null) {
    236            cpString = new CPString(cpUTF8Value(utf8Index), globalIndex);
    237            stringsToCPStrings.put(string, cpString);
    238        }
    239        return cpString;
    240    }
    241
    242    public CPUTF8 cpUTF8Value(final int index) {
    243        final String string = cpUTF8[index];
    244        CPUTF8 cputf8 = stringsToCPUTF8.get(string);
    245        if (cputf8 == null) {
    246            cputf8 = new CPUTF8(string, index);
    247            stringsToCPUTF8.put(string, cputf8);
    248        } else if (cputf8.getGlobalIndex() > index) {
    249            cputf8.setGlobalIndex(index);
    250        }
    251        return cputf8;
    252    }
    253
    254    public CPUTF8 cpUTF8Value(final String string) {
    255        return cpUTF8Value(string, true);
    256    }
    257
    258    public CPUTF8 cpUTF8Value(final String string, final boolean searchForIndex) {
    259        CPUTF8 cputf8 = stringsToCPUTF8.get(string);
    260        if (cputf8 == null) {
    261            Integer index = null;
    262            if (searchForIndex) {
    263                index = mapUTF8.get(string);
    264            }
    265            if (index != null) {
    266                return cpUTF8Value(index.intValue());
    267            }
    268            if (searchForIndex) {
    269                index = mapSignature.get(string);
    270            }
    271            if (index != null) {
    272                return cpSignatureValue(index.intValue());
    273            }
    274            cputf8 = new CPUTF8(string, -1);
    275            stringsToCPUTF8.put(string, cputf8);
    276        }
    277        return cputf8;
    278    }
    279
    280    public SegmentConstantPool getConstantPool() {
    281        return pool;
    282    }
    283
    284    public String[] getCpClass() {
    285        return cpClass;
    286    }
    287
    288    public String[] getCpDescriptor() {
    289        return cpDescriptor;
    290    }
    291
    292    public int[] getCpDescriptorNameInts() {
    293        return cpDescriptorNameInts;
    294    }
    295
    296    public int[] getCpDescriptorTypeInts() {
    297        return cpDescriptorTypeInts;
    298    }
    299
    300    public String[] getCpFieldClass() {
    301        return cpFieldClass;
    302    }
    303
    304    public String[] getCpIMethodClass() {
    305        return cpIMethodClass;
    306    }
    307
    308    public int[] getCpInt() {
    309        return cpInt;
    310    }
    311
    312    public long[] getCpLong() {
    313        return cpLong;
    314    }
    315
    316    public String[] getCpMethodClass() {
    317        return cpMethodClass;
    318    }
    319
    320    public String[] getCpMethodDescriptor() {
    321        return cpMethodDescriptor;
    322    }
    323
    324    public String[] getCpSignature() {
    325        return cpSignature;
    326    }
    327
    328    public String[] getCpUTF8() {
    329        return cpUTF8;
    330    }
    331
    332    /**
    333     * Parses the constant pool class names, using {@link #cpClassCount} to populate {@link #cpClass} from {@link #cpUTF8}.
    334     *
    335     * @param in the input stream to read from
    336     * @throws IOException      if a problem occurs during reading from the underlying stream
    337     * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported codec
    338     */
    339    private void parseCpClass(final InputStream in) throws IOException, Pack200Exception {
    340        final int cpClassCount = header.getCpClassCount();
    341        cpClassInts = decodeBandInt("cp_Class", in, Codec.UDELTA5, cpClassCount);
    342        cpClass = new String[cpClassCount];
    343        mapClass = new HashMap<>(cpClassCount);
    344        for (int i = 0; i < cpClassCount; i++) {
    345            cpClass[i] = cpUTF8[cpClassInts[i]];
    346            mapClass.put(cpClass[i], Integer.valueOf(i));
    347        }
    348    }
    349
    350    /**
    351     * Parses the constant pool descriptor definitions, using {@link #cpDescriptorCount} to populate {@link #cpDescriptor}. For ease of use, the cpDescriptor is
    352     * stored as a string of the form <em>name:type</em>, largely to make it easier for representing field and method descriptors (e.g.
    353     * {@code out:java.lang.PrintStream}) in a way that is compatible with passing String arrays.
    354     *
    355     * @param in the input stream to read from
    356     * @throws IOException      if a problem occurs during reading from the underlying stream
    357     * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported codec
    358     */
    359    private void parseCpDescriptor(final InputStream in) throws IOException, Pack200Exception {
    360        final int cpDescriptorCount = header.getCpDescriptorCount();
    361        cpDescriptorNameInts = decodeBandInt("cp_Descr_name", in, Codec.DELTA5, cpDescriptorCount);
    362        cpDescriptorTypeInts = decodeBandInt("cp_Descr_type", in, Codec.UDELTA5, cpDescriptorCount);
    363        final String[] cpDescriptorNames = getReferences(cpDescriptorNameInts, cpUTF8);
    364        final String[] cpDescriptorTypes = getReferences(cpDescriptorTypeInts, cpSignature);
    365        cpDescriptor = new String[cpDescriptorCount];
    366        mapDescriptor = new HashMap<>(cpDescriptorCount);
    367        for (int i = 0; i < cpDescriptorCount; i++) {
    368            cpDescriptor[i] = cpDescriptorNames[i] + ":" + cpDescriptorTypes[i]; //$NON-NLS-1$
    369            mapDescriptor.put(cpDescriptor[i], Integer.valueOf(i));
    370        }
    371    }
    372
    373    private void parseCpDouble(final InputStream in) throws IOException, Pack200Exception {
    374        final int cpDoubleCount = header.getCpDoubleCount();
    375        final long[] band = parseFlags("cp_Double", in, cpDoubleCount, Codec.UDELTA5, Codec.DELTA5);
    376        cpDouble = new double[band.length];
    377        Arrays.setAll(cpDouble, i -> Double.longBitsToDouble(band[i]));
    378    }
    379
    380    /**
    381     * Parses the constant pool field definitions, using {@link #cpFieldCount} to populate {@link #cpFieldClass} and {@link #cpFieldDescriptor}.
    382     *
    383     * @param in the input stream to read from
    384     * @throws IOException      if a problem occurs during reading from the underlying stream
    385     * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported codec
    386     */
    387    private void parseCpField(final InputStream in) throws IOException, Pack200Exception {
    388        final int cpFieldCount = header.getCpFieldCount();
    389        cpFieldClassInts = decodeBandInt("cp_Field_class", in, Codec.DELTA5, cpFieldCount);
    390        cpFieldDescriptorInts = decodeBandInt("cp_Field_desc", in, Codec.UDELTA5, cpFieldCount);
    391        cpFieldClass = new String[cpFieldCount];
    392        cpFieldDescriptor = new String[cpFieldCount];
    393        for (int i = 0; i < cpFieldCount; i++) {
    394            cpFieldClass[i] = cpClass[cpFieldClassInts[i]];
    395            cpFieldDescriptor[i] = cpDescriptor[cpFieldDescriptorInts[i]];
    396        }
    397    }
    398
    399    private void parseCpFloat(final InputStream in) throws IOException, Pack200Exception {
    400        final int cpFloatCount = header.getCpFloatCount();
    401        final int[] floatBits = decodeBandInt("cp_Float", in, Codec.UDELTA5, cpFloatCount);
    402        cpFloat = new float[cpFloatCount];
    403        for (int i = 0; i < cpFloatCount; i++) {
    404            cpFloat[i] = Float.intBitsToFloat(floatBits[i]);
    405        }
    406    }
    407
    408    /**
    409     * Parses the constant pool interface method definitions, using {@link #cpIMethodCount} to populate {@link #cpIMethodClass} and
    410     * {@link #cpIMethodDescriptor}.
    411     *
    412     * @param in the input stream to read from
    413     * @throws IOException      if a problem occurs during reading from the underlying stream
    414     * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported codec
    415     */
    416    private void parseCpIMethod(final InputStream in) throws IOException, Pack200Exception {
    417        final int cpIMethodCount = header.getCpIMethodCount();
    418        cpIMethodClassInts = decodeBandInt("cp_Imethod_class", in, Codec.DELTA5, cpIMethodCount);
    419        cpIMethodDescriptorInts = decodeBandInt("cp_Imethod_desc", in, Codec.UDELTA5, cpIMethodCount);
    420        cpIMethodClass = new String[cpIMethodCount];
    421        cpIMethodDescriptor = new String[cpIMethodCount];
    422        for (int i = 0; i < cpIMethodCount; i++) {
    423            cpIMethodClass[i] = cpClass[cpIMethodClassInts[i]];
    424            cpIMethodDescriptor[i] = cpDescriptor[cpIMethodDescriptorInts[i]];
    425        }
    426    }
    427
    428    private void parseCpInt(final InputStream in) throws IOException, Pack200Exception {
    429        final int cpIntCount = header.getCpIntCount();
    430        cpInt = decodeBandInt("cpInt", in, Codec.UDELTA5, cpIntCount);
    431    }
    432
    433    private void parseCpLong(final InputStream in) throws IOException, Pack200Exception {
    434        final int cpLongCount = header.getCpLongCount();
    435        cpLong = parseFlags("cp_Long", in, cpLongCount, Codec.UDELTA5, Codec.DELTA5);
    436    }
    437
    438    /**
    439     * Parses the constant pool method definitions, using {@link #cpMethodCount} to populate {@link #cpMethodClass} and {@link #cpMethodDescriptor}.
    440     *
    441     * @param in the input stream to read from
    442     * @throws IOException      if a problem occurs during reading from the underlying stream
    443     * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported codec
    444     */
    445    private void parseCpMethod(final InputStream in) throws IOException, Pack200Exception {
    446        final int cpMethodCount = header.getCpMethodCount();
    447        cpMethodClassInts = decodeBandInt("cp_Method_class", in, Codec.DELTA5, cpMethodCount);
    448        cpMethodDescriptorInts = decodeBandInt("cp_Method_desc", in, Codec.UDELTA5, cpMethodCount);
    449        cpMethodClass = new String[cpMethodCount];
    450        cpMethodDescriptor = new String[cpMethodCount];
    451        for (int i = 0; i < cpMethodCount; i++) {
    452            cpMethodClass[i] = cpClass[cpMethodClassInts[i]];
    453            cpMethodDescriptor[i] = cpDescriptor[cpMethodDescriptorInts[i]];
    454        }
    455    }
    456
    457    /**
    458     * Parses the constant pool signature classes, using {@link #cpSignatureCount} to populate {@link #cpSignature}. A signature form is akin to the bytecode
    459     * representation of a class; Z for boolean, I for int, [ for array etc. However, although classes are started with L, the class name does not follow the
    460     * form; instead, there is a separate array of classes. So an array corresponding to {@code public static void main(String args[])} has a form of
    461     * {@code [L(V)} and a classes array of {@code [java.lang.String]}. The {@link #cpSignature} is a string representation identical to the bytecode equivalent
    462     * {@code [Ljava/lang/String;(V)} TODO Check that the form is as above and update other types e.g. J
    463     *
    464     * @param in the input stream to read from
    465     * @throws IOException      if a problem occurs during reading from the underlying stream
    466     * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported codec
    467     */
    468    private void parseCpSignature(final InputStream in) throws IOException, Pack200Exception {
    469        final int cpSignatureCount = header.getCpSignatureCount();
    470        cpSignatureInts = decodeBandInt("cp_Signature_form", in, Codec.DELTA5, cpSignatureCount);
    471        final String[] cpSignatureForm = getReferences(cpSignatureInts, cpUTF8);
    472        cpSignature = new String[cpSignatureCount];
    473        mapSignature = new HashMap<>();
    474        int lCount = 0;
    475        for (int i = 0; i < cpSignatureCount; i++) {
    476            final String form = cpSignatureForm[i];
    477            final char[] chars = form.toCharArray();
    478            for (final char element : chars) {
    479                if (element == 'L') {
    480                    cpSignatureInts[i] = -1;
    481                    lCount++;
    482                }
    483            }
    484        }
    485        final String[] cpSignatureClasses = parseReferences("cp_Signature_classes", in, Codec.UDELTA5, lCount, cpClass);
    486        int index = 0;
    487        for (int i = 0; i < cpSignatureCount; i++) {
    488            final String form = cpSignatureForm[i];
    489            final int len = form.length();
    490            final StringBuilder signature = new StringBuilder(64);
    491            for (int j = 0; j < len; j++) {
    492                final char c = form.charAt(j);
    493                signature.append(c);
    494                if (c == 'L') {
    495                    final String className = cpSignatureClasses[index];
    496                    signature.append(className);
    497                    index++;
    498                }
    499            }
    500            cpSignature[i] = signature.toString();
    501            mapSignature.put(signature.toString(), Integer.valueOf(i));
    502        }
    503//        for (int i = 0; i < cpSignatureInts.length; i++) {
    504//            if (cpSignatureInts[i] == -1) {
    505//                cpSignatureInts[i] = search(cpUTF8, cpSignature[i]);
    506//            }
    507//        }
    508    }
    509
    510    /**
    511     * Parses the constant pool strings, using {@link #cpStringCount} to populate {@link #cpString} from indexes into {@link #cpUTF8}.
    512     *
    513     * @param in the input stream to read from
    514     * @throws IOException      if a problem occurs during reading from the underlying stream
    515     * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported codec
    516     */
    517    private void parseCpString(final InputStream in) throws IOException, Pack200Exception {
    518        final int cpStringCount = header.getCpStringCount();
    519        cpStringInts = decodeBandInt("cp_String", in, Codec.UDELTA5, cpStringCount);
    520        cpString = new String[cpStringCount];
    521        Arrays.setAll(cpString, i -> cpUTF8[cpStringInts[i]]);
    522    }
    523
    524    private void parseCpUtf8(final InputStream in) throws IOException, Pack200Exception {
    525        final int cpUTF8Count = header.getCpUTF8Count();
    526        if (cpUTF8Count <= 0) {
    527            throw new IOException("cpUTF8Count value must be greater than 0");
    528        }
    529        final int[] prefix = decodeBandInt("cpUTF8Prefix", in, Codec.DELTA5, cpUTF8Count - 2);
    530        int charCount = 0;
    531        int bigSuffixCount = 0;
    532        final int[] suffix = decodeBandInt("cpUTF8Suffix", in, Codec.UNSIGNED5, cpUTF8Count - 1);
    533
    534        for (final int element : suffix) {
    535            if (element == 0) {
    536                bigSuffixCount++;
    537            } else {
    538                charCount += element;
    539            }
    540        }
    541        final int[] dataBand = decodeBandInt("cp_Utf8_chars", in, Codec.CHAR3, charCount);
    542        final char[] data = new char[charCount];
    543        for (int i = 0; i < data.length; i++) {
    544            data[i] = (char) dataBand[i];
    545        }
    546
    547        // Read in the big suffix data
    548        final int[] bigSuffixCounts = decodeBandInt("cp_Utf8_big_suffix", in, Codec.DELTA5, bigSuffixCount);
    549        final int[][] bigSuffixDataBand = new int[bigSuffixCount][];
    550        for (int i = 0; i < bigSuffixDataBand.length; i++) {
    551            bigSuffixDataBand[i] = decodeBandInt("cp_Utf8_big_chars " + i, in, Codec.DELTA5, bigSuffixCounts[i]);
    552        }
    553
    554        // Convert big suffix data to characters
    555        final char[][] bigSuffixData = new char[bigSuffixCount][];
    556        for (int i = 0; i < bigSuffixDataBand.length; i++) {
    557            bigSuffixData[i] = new char[bigSuffixDataBand[i].length];
    558            for (int j = 0; j < bigSuffixDataBand[i].length; j++) {
    559                bigSuffixData[i][j] = (char) bigSuffixDataBand[i][j];
    560            }
    561        }
    562
    563        // Initialize variables
    564        mapUTF8 = new HashMap<>(cpUTF8Count + 1);
    565        cpUTF8 = new String[cpUTF8Count];
    566        cpUTF8[0] = EMPTY_STRING;
    567        mapUTF8.put(EMPTY_STRING, Integer.valueOf(0));
    568
    569        // Go through the strings
    570        charCount = 0;
    571        bigSuffixCount = 0;
    572        for (int i = 1; i < cpUTF8Count; i++) {
    573            final String lastString = cpUTF8[i - 1];
    574            if (suffix[i - 1] == 0) {
    575                // The big suffix stuff hasn't been tested, and I'll be
    576                // surprised if it works first time w/o errors ...
    577                cpUTF8[i] = lastString.substring(0, i > 1 ? prefix[i - 2] : 0) + new String(bigSuffixData[bigSuffixCount++]);
    578                mapUTF8.put(cpUTF8[i], Integer.valueOf(i));
    579            } else {
    580                cpUTF8[i] = lastString.substring(0, i > 1 ? prefix[i - 2] : 0) + new String(data, charCount, suffix[i - 1]);
    581                charCount += suffix[i - 1];
    582                mapUTF8.put(cpUTF8[i], Integer.valueOf(i));
    583            }
    584        }
    585    }
    586
    587    @Override
    588    public void read(final InputStream in) throws IOException, Pack200Exception {
    589        parseCpUtf8(in);
    590        parseCpInt(in);
    591        parseCpFloat(in);
    592        parseCpLong(in);
    593        parseCpDouble(in);
    594        parseCpString(in);
    595        parseCpClass(in);
    596        parseCpSignature(in);
    597        parseCpDescriptor(in);
    598        parseCpField(in);
    599        parseCpMethod(in);
    600        parseCpIMethod(in);
    601
    602        intOffset = cpUTF8.length;
    603        floatOffset = intOffset + cpInt.length;
    604        longOffset = floatOffset + cpFloat.length;
    605        doubleOffset = longOffset + cpLong.length;
    606        stringOffset = doubleOffset + cpDouble.length;
    607        classOffset = stringOffset + cpString.length;
    608        signatureOffset = classOffset + cpClass.length;
    609        descrOffset = signatureOffset + cpSignature.length;
    610        fieldOffset = descrOffset + cpDescriptor.length;
    611        methodOffset = fieldOffset + cpFieldClass.length;
    612        imethodOffset = methodOffset + cpMethodClass.length;
    613    }
    614
    615    @Override
    616    public void unpack() {
    617
    618    }
    619
    620}