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