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