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