View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one or more
3    *  contributor license agreements.  See the NOTICE file distributed with
4    *  this work for additional information regarding copyright ownership.
5    *  The ASF licenses this file to You under the Apache License, Version 2.0
6    *  (the "License"); you may not use this file except in compliance with
7    *  the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  package org.apache.commons.compress.harmony.unpack200;
18  
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.util.Arrays;
22  import java.util.HashMap;
23  import java.util.Map;
24  
25  import org.apache.commons.compress.harmony.pack200.Codec;
26  import org.apache.commons.compress.harmony.pack200.Pack200Exception;
27  import org.apache.commons.compress.harmony.unpack200.bytecode.CPClass;
28  import org.apache.commons.compress.harmony.unpack200.bytecode.CPDouble;
29  import org.apache.commons.compress.harmony.unpack200.bytecode.CPFieldRef;
30  import org.apache.commons.compress.harmony.unpack200.bytecode.CPFloat;
31  import org.apache.commons.compress.harmony.unpack200.bytecode.CPInteger;
32  import org.apache.commons.compress.harmony.unpack200.bytecode.CPInterfaceMethodRef;
33  import org.apache.commons.compress.harmony.unpack200.bytecode.CPLong;
34  import org.apache.commons.compress.harmony.unpack200.bytecode.CPMethodRef;
35  import org.apache.commons.compress.harmony.unpack200.bytecode.CPNameAndType;
36  import org.apache.commons.compress.harmony.unpack200.bytecode.CPString;
37  import org.apache.commons.compress.harmony.unpack200.bytecode.CPUTF8;
38  
39  /**
40   * Constant Pool bands
41   */
42  public class CpBands extends BandSet {
43  
44      private static final String EMPTY_STRING = ""; //$NON-NLS-1$
45  
46      private final SegmentConstantPool pool = new SegmentConstantPool(this);
47  
48      private String[] cpClass;
49  
50      private int[] cpClassInts;
51      private int[] cpDescriptorNameInts;
52      private int[] cpDescriptorTypeInts;
53      private String[] cpDescriptor;
54      private double[] cpDouble;
55      private String[] cpFieldClass;
56      private String[] cpFieldDescriptor;
57      private int[] cpFieldClassInts;
58      private int[] cpFieldDescriptorInts;
59      private float[] cpFloat;
60      private String[] cpIMethodClass;
61      private String[] cpIMethodDescriptor;
62      private int[] cpIMethodClassInts;
63      private int[] cpIMethodDescriptorInts;
64      private int[] cpInt;
65      private long[] cpLong;
66      private String[] cpMethodClass;
67      private String[] cpMethodDescriptor;
68      private int[] cpMethodClassInts;
69      private int[] cpMethodDescriptorInts;
70      private String[] cpSignature;
71      private int[] cpSignatureInts;
72      private String[] cpString;
73      private int[] cpStringInts;
74      private String[] cpUTF8;
75      private final Map<String, CPUTF8> stringsToCPUTF8 = new HashMap<>();
76  
77      private final Map<String, CPString> stringsToCPStrings = new HashMap<>();
78      private final Map<Long, CPLong> longsToCPLongs = new HashMap<>();
79      private final Map<Integer, CPInteger> integersToCPIntegers = new HashMap<>();
80      private final Map<Float, CPFloat> floatsToCPFloats = new HashMap<>();
81      private final Map<String, CPClass> stringsToCPClass = new HashMap<>();
82      private final Map<Double, CPDouble> doublesToCPDoubles = new HashMap<>();
83      private final Map<String, CPNameAndType> descriptorsToCPNameAndTypes = new HashMap<>();
84      private Map<String, Integer> mapClass;
85  
86      private Map<String, Integer> mapDescriptor;
87      private Map<String, Integer> mapUTF8;
88      // TODO: Not used
89      private Map<String, Integer> mapSignature;
90  
91      private int intOffset;
92  
93      private int floatOffset;
94      private int longOffset;
95      private int doubleOffset;
96      private int stringOffset;
97      private int classOffset;
98      private int signatureOffset;
99      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 }