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