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}