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.ArrayList;
22  import java.util.Arrays;
23  import java.util.HashMap;
24  import java.util.Map;
25  
26  import org.apache.commons.compress.harmony.pack200.Codec;
27  import org.apache.commons.compress.harmony.pack200.Pack200Exception;
28  import org.apache.commons.compress.harmony.unpack200.bytecode.CPClass;
29  import org.apache.commons.compress.harmony.unpack200.bytecode.CPDouble;
30  import org.apache.commons.compress.harmony.unpack200.bytecode.CPFieldRef;
31  import org.apache.commons.compress.harmony.unpack200.bytecode.CPFloat;
32  import org.apache.commons.compress.harmony.unpack200.bytecode.CPInteger;
33  import org.apache.commons.compress.harmony.unpack200.bytecode.CPInterfaceMethodRef;
34  import org.apache.commons.compress.harmony.unpack200.bytecode.CPLong;
35  import org.apache.commons.compress.harmony.unpack200.bytecode.CPMethodRef;
36  import org.apache.commons.compress.harmony.unpack200.bytecode.CPNameAndType;
37  import org.apache.commons.compress.harmony.unpack200.bytecode.CPString;
38  import org.apache.commons.compress.harmony.unpack200.bytecode.CPUTF8;
39  
40  /**
41   * Constant Pool bands
42   */
43  public class CpBands extends BandSet {
44  
45      private static final String EMPTY_STRING = ""; //$NON-NLS-1$
46  
47      private final SegmentConstantPool pool = new SegmentConstantPool(this);
48  
49      private String[] cpClass;
50  
51      private int[] cpClassInts;
52      private int[] cpDescriptorNameInts;
53      private int[] cpDescriptorTypeInts;
54      private String[] cpDescriptor;
55      private double[] cpDouble;
56      private String[] cpFieldClass;
57      private String[] cpFieldDescriptor;
58      private int[] cpFieldClassInts;
59      private int[] cpFieldDescriptorInts;
60      private float[] cpFloat;
61      private String[] cpIMethodClass;
62      private String[] cpIMethodDescriptor;
63      private int[] cpIMethodClassInts;
64      private int[] cpIMethodDescriptorInts;
65      private int[] cpInt;
66      private long[] cpLong;
67      private String[] cpMethodClass;
68      private String[] cpMethodDescriptor;
69      private int[] cpMethodClassInts;
70      private int[] cpMethodDescriptorInts;
71      private String[] cpSignature;
72      private int[] cpSignatureInts;
73      private String[] cpString;
74      private int[] cpStringInts;
75      private String[] cpUTF8;
76      private final Map<String, CPUTF8> stringsToCPUTF8 = new HashMap<>();
77  
78      private final Map<String, CPString> stringsToCPStrings = new HashMap<>();
79      private final Map<Long, CPLong> longsToCPLongs = new HashMap<>();
80      private final Map<Integer, CPInteger> integersToCPIntegers = new HashMap<>();
81      private final Map<Float, CPFloat> floatsToCPFloats = new HashMap<>();
82      private final Map<String, CPClass> stringsToCPClass = new HashMap<>();
83      private final Map<Double, CPDouble> doublesToCPDoubles = new HashMap<>();
84      private final Map<String, CPNameAndType> descriptorsToCPNameAndTypes = new HashMap<>();
85      private Map<String, Integer> mapClass;
86  
87      private Map<String, Integer> mapDescriptor;
88      private Map<String, Integer> mapUTF8;
89      // TODO: Not used
90      private Map<String, Integer> mapSignature;
91  
92      private int intOffset;
93  
94      private int floatOffset;
95      private int longOffset;
96      private int doubleOffset;
97      private int stringOffset;
98      private int classOffset;
99      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 }