CpBands.java

  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. import java.io.IOException;
  19. import java.io.InputStream;
  20. import java.util.Arrays;
  21. import java.util.HashMap;
  22. import java.util.Map;

  23. import org.apache.commons.compress.harmony.pack200.Codec;
  24. import org.apache.commons.compress.harmony.pack200.Pack200Exception;
  25. import org.apache.commons.compress.harmony.unpack200.bytecode.CPClass;
  26. import org.apache.commons.compress.harmony.unpack200.bytecode.CPDouble;
  27. import org.apache.commons.compress.harmony.unpack200.bytecode.CPFieldRef;
  28. import org.apache.commons.compress.harmony.unpack200.bytecode.CPFloat;
  29. import org.apache.commons.compress.harmony.unpack200.bytecode.CPInteger;
  30. import org.apache.commons.compress.harmony.unpack200.bytecode.CPInterfaceMethodRef;
  31. import org.apache.commons.compress.harmony.unpack200.bytecode.CPLong;
  32. import org.apache.commons.compress.harmony.unpack200.bytecode.CPMethodRef;
  33. import org.apache.commons.compress.harmony.unpack200.bytecode.CPNameAndType;
  34. import org.apache.commons.compress.harmony.unpack200.bytecode.CPString;
  35. import org.apache.commons.compress.harmony.unpack200.bytecode.CPUTF8;

  36. /**
  37.  * Constant Pool bands
  38.  */
  39. public class CpBands extends BandSet {

  40.     private static final String EMPTY_STRING = ""; //$NON-NLS-1$

  41.     private final SegmentConstantPool pool = new SegmentConstantPool(this);

  42.     private String[] cpClass;

  43.     private int[] cpClassInts;
  44.     private int[] cpDescriptorNameInts;
  45.     private int[] cpDescriptorTypeInts;
  46.     private String[] cpDescriptor;
  47.     private double[] cpDouble;
  48.     private String[] cpFieldClass;
  49.     private String[] cpFieldDescriptor;
  50.     private int[] cpFieldClassInts;
  51.     private int[] cpFieldDescriptorInts;
  52.     private float[] cpFloat;
  53.     private String[] cpIMethodClass;
  54.     private String[] cpIMethodDescriptor;
  55.     private int[] cpIMethodClassInts;
  56.     private int[] cpIMethodDescriptorInts;
  57.     private int[] cpInt;
  58.     private long[] cpLong;
  59.     private String[] cpMethodClass;
  60.     private String[] cpMethodDescriptor;
  61.     private int[] cpMethodClassInts;
  62.     private int[] cpMethodDescriptorInts;
  63.     private String[] cpSignature;
  64.     private int[] cpSignatureInts;
  65.     private String[] cpString;
  66.     private int[] cpStringInts;
  67.     private String[] cpUTF8;
  68.     private final Map<String, CPUTF8> stringsToCPUTF8 = new HashMap<>();

  69.     private final Map<String, CPString> stringsToCPStrings = new HashMap<>();
  70.     private final Map<Long, CPLong> longsToCPLongs = new HashMap<>();
  71.     private final Map<Integer, CPInteger> integersToCPIntegers = new HashMap<>();
  72.     private final Map<Float, CPFloat> floatsToCPFloats = new HashMap<>();
  73.     private final Map<String, CPClass> stringsToCPClass = new HashMap<>();
  74.     private final Map<Double, CPDouble> doublesToCPDoubles = new HashMap<>();
  75.     private final Map<String, CPNameAndType> descriptorsToCPNameAndTypes = new HashMap<>();
  76.     private Map<String, Integer> mapClass;

  77.     private Map<String, Integer> mapDescriptor;
  78.     private Map<String, Integer> mapUTF8;
  79.     // TODO: Not used
  80.     private Map<String, Integer> mapSignature;

  81.     private int intOffset;

  82.     private int floatOffset;
  83.     private int longOffset;
  84.     private int doubleOffset;
  85.     private int stringOffset;
  86.     private int classOffset;
  87.     private int signatureOffset;
  88.     private int descrOffset;
  89.     private int fieldOffset;
  90.     private int methodOffset;
  91.     private int imethodOffset;

  92.     public CpBands(final Segment segment) {
  93.         super(segment);
  94.     }

  95.     public CPClass cpClassValue(final int index) {
  96.         final String string = cpClass[index];
  97.         final int utf8Index = cpClassInts[index];
  98.         final int globalIndex = classOffset + index;
  99.         return stringsToCPClass.computeIfAbsent(string, k -> new CPClass(cpUTF8Value(utf8Index), globalIndex));
  100.     }

  101.     public CPClass cpClassValue(final String string) {
  102.         CPClass cpString = stringsToCPClass.get(string);
  103.         if (cpString == null) {
  104.             final Integer index = mapClass.get(string);
  105.             if (index != null) {
  106.                 return cpClassValue(index.intValue());
  107.             }
  108.             cpString = new CPClass(cpUTF8Value(string, false), -1);
  109.             stringsToCPClass.put(string, cpString);
  110.         }
  111.         return cpString;
  112.     }

  113.     public CPDouble cpDoubleValue(final int index) {
  114.         final Double dbl = Double.valueOf(cpDouble[index]);
  115.         CPDouble cpDouble = doublesToCPDoubles.get(dbl);
  116.         if (cpDouble == null) {
  117.             cpDouble = new CPDouble(dbl, index + doubleOffset);
  118.             doublesToCPDoubles.put(dbl, cpDouble);
  119.         }
  120.         return cpDouble;
  121.     }

  122.     public CPFieldRef cpFieldValue(final int index) {
  123.         return new CPFieldRef(cpClassValue(cpFieldClassInts[index]), cpNameAndTypeValue(cpFieldDescriptorInts[index]), index + fieldOffset);
  124.     }

  125.     public CPFloat cpFloatValue(final int index) {
  126.         final Float f = Float.valueOf(cpFloat[index]);
  127.         CPFloat cpFloat = floatsToCPFloats.get(f);
  128.         if (cpFloat == null) {
  129.             cpFloat = new CPFloat(f, index + floatOffset);
  130.             floatsToCPFloats.put(f, cpFloat);
  131.         }
  132.         return cpFloat;
  133.     }

  134.     public CPInterfaceMethodRef cpIMethodValue(final int index) {
  135.         return new CPInterfaceMethodRef(cpClassValue(cpIMethodClassInts[index]), cpNameAndTypeValue(cpIMethodDescriptorInts[index]), index + imethodOffset);
  136.     }

  137.     public CPInteger cpIntegerValue(final int index) {
  138.         final Integer i = Integer.valueOf(cpInt[index]);
  139.         CPInteger cpInteger = integersToCPIntegers.get(i);
  140.         if (cpInteger == null) {
  141.             cpInteger = new CPInteger(i, index + intOffset);
  142.             integersToCPIntegers.put(i, cpInteger);
  143.         }
  144.         return cpInteger;
  145.     }

  146.     public CPLong cpLongValue(final int index) {
  147.         final Long l = Long.valueOf(cpLong[index]);
  148.         CPLong cpLong = longsToCPLongs.get(l);
  149.         if (cpLong == null) {
  150.             cpLong = new CPLong(l, index + longOffset);
  151.             longsToCPLongs.put(l, cpLong);
  152.         }
  153.         return cpLong;
  154.     }

  155.     public CPMethodRef cpMethodValue(final int index) {
  156.         return new CPMethodRef(cpClassValue(cpMethodClassInts[index]), cpNameAndTypeValue(cpMethodDescriptorInts[index]), index + methodOffset);
  157.     }

  158.     public CPNameAndType cpNameAndTypeValue(final int index) {
  159.         final String descriptor = cpDescriptor[index];
  160.         CPNameAndType cpNameAndType = descriptorsToCPNameAndTypes.get(descriptor);
  161.         if (cpNameAndType == null) {
  162.             final int nameIndex = cpDescriptorNameInts[index];
  163.             final int descriptorIndex = cpDescriptorTypeInts[index];

  164.             final CPUTF8 name = cpUTF8Value(nameIndex);
  165.             final CPUTF8 descriptorU = cpSignatureValue(descriptorIndex);
  166.             cpNameAndType = new CPNameAndType(name, descriptorU, index + descrOffset);
  167.             descriptorsToCPNameAndTypes.put(descriptor, cpNameAndType);
  168.         }
  169.         return cpNameAndType;
  170.     }

  171.     public CPNameAndType cpNameAndTypeValue(final String descriptor) {
  172.         CPNameAndType cpNameAndType = descriptorsToCPNameAndTypes.get(descriptor);
  173.         if (cpNameAndType == null) {
  174.             final Integer index = mapDescriptor.get(descriptor);
  175.             if (index != null) {
  176.                 return cpNameAndTypeValue(index.intValue());
  177.             }
  178.             final int colon = descriptor.indexOf(':');
  179.             final String nameString = descriptor.substring(0, colon);
  180.             final String descriptorString = descriptor.substring(colon + 1);

  181.             final CPUTF8 name = cpUTF8Value(nameString, true);
  182.             final CPUTF8 descriptorU = cpUTF8Value(descriptorString, true);
  183.             cpNameAndType = new CPNameAndType(name, descriptorU, -1 + descrOffset);
  184.             descriptorsToCPNameAndTypes.put(descriptor, cpNameAndType);
  185.         }
  186.         return cpNameAndType;
  187.     }

  188.     public CPUTF8 cpSignatureValue(final int index) {
  189.         int globalIndex;
  190.         if (cpSignatureInts[index] != -1) {
  191.             globalIndex = cpSignatureInts[index];
  192.         } else {
  193.             globalIndex = index + signatureOffset;
  194.         }
  195.         final String string = cpSignature[index];
  196.         CPUTF8 cpUTF8 = stringsToCPUTF8.get(string);
  197.         if (cpUTF8 == null) {
  198.             cpUTF8 = new CPUTF8(string, globalIndex);
  199.             stringsToCPUTF8.put(string, cpUTF8);
  200.         }
  201.         return cpUTF8;
  202.     }

  203.     public CPString cpStringValue(final int index) {
  204.         final String string = cpString[index];
  205.         final int utf8Index = cpStringInts[index];
  206.         final int globalIndex = stringOffset + index;
  207.         CPString cpString = stringsToCPStrings.get(string);
  208.         if (cpString == null) {
  209.             cpString = new CPString(cpUTF8Value(utf8Index), globalIndex);
  210.             stringsToCPStrings.put(string, cpString);
  211.         }
  212.         return cpString;
  213.     }

  214.     public CPUTF8 cpUTF8Value(final int index) {
  215.         final String string = cpUTF8[index];
  216.         CPUTF8 cputf8 = stringsToCPUTF8.get(string);
  217.         if (cputf8 == null) {
  218.             cputf8 = new CPUTF8(string, index);
  219.             stringsToCPUTF8.put(string, cputf8);
  220.         } else if (cputf8.getGlobalIndex() > index) {
  221.             cputf8.setGlobalIndex(index);
  222.         }
  223.         return cputf8;
  224.     }

  225.     public CPUTF8 cpUTF8Value(final String string) {
  226.         return cpUTF8Value(string, true);
  227.     }

  228.     public CPUTF8 cpUTF8Value(final String string, final boolean searchForIndex) {
  229.         CPUTF8 cputf8 = stringsToCPUTF8.get(string);
  230.         if (cputf8 == null) {
  231.             Integer index = null;
  232.             if (searchForIndex) {
  233.                 index = mapUTF8.get(string);
  234.             }
  235.             if (index != null) {
  236.                 return cpUTF8Value(index.intValue());
  237.             }
  238.             if (searchForIndex) {
  239.                 index = mapSignature.get(string);
  240.             }
  241.             if (index != null) {
  242.                 return cpSignatureValue(index.intValue());
  243.             }
  244.             cputf8 = new CPUTF8(string, -1);
  245.             stringsToCPUTF8.put(string, cputf8);
  246.         }
  247.         return cputf8;
  248.     }

  249.     public SegmentConstantPool getConstantPool() {
  250.         return pool;
  251.     }

  252.     public String[] getCpClass() {
  253.         return cpClass;
  254.     }

  255.     public String[] getCpDescriptor() {
  256.         return cpDescriptor;
  257.     }

  258.     public int[] getCpDescriptorNameInts() {
  259.         return cpDescriptorNameInts;
  260.     }

  261.     public int[] getCpDescriptorTypeInts() {
  262.         return cpDescriptorTypeInts;
  263.     }

  264.     public String[] getCpFieldClass() {
  265.         return cpFieldClass;
  266.     }

  267.     public String[] getCpIMethodClass() {
  268.         return cpIMethodClass;
  269.     }

  270.     public int[] getCpInt() {
  271.         return cpInt;
  272.     }

  273.     public long[] getCpLong() {
  274.         return cpLong;
  275.     }

  276.     public String[] getCpMethodClass() {
  277.         return cpMethodClass;
  278.     }

  279.     public String[] getCpMethodDescriptor() {
  280.         return cpMethodDescriptor;
  281.     }

  282.     public String[] getCpSignature() {
  283.         return cpSignature;
  284.     }

  285.     public String[] getCpUTF8() {
  286.         return cpUTF8;
  287.     }

  288.     /**
  289.      * Parses the constant pool class names, using {@link #cpClassCount} to populate {@link #cpClass} from {@link #cpUTF8}.
  290.      *
  291.      * @param in the input stream to read from
  292.      * @throws IOException      if a problem occurs during reading from the underlying stream
  293.      * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported codec
  294.      */
  295.     private void parseCpClass(final InputStream in) throws IOException, Pack200Exception {
  296.         final int cpClassCount = header.getCpClassCount();
  297.         cpClassInts = decodeBandInt("cp_Class", in, Codec.UDELTA5, cpClassCount);
  298.         cpClass = new String[cpClassCount];
  299.         mapClass = new HashMap<>(cpClassCount);
  300.         for (int i = 0; i < cpClassCount; i++) {
  301.             cpClass[i] = cpUTF8[cpClassInts[i]];
  302.             mapClass.put(cpClass[i], Integer.valueOf(i));
  303.         }
  304.     }

  305.     /**
  306.      * Parses the constant pool descriptor definitions, using {@link #cpDescriptorCount} to populate {@link #cpDescriptor}. For ease of use, the cpDescriptor is
  307.      * stored as a string of the form <em>name:type</em>, largely to make it easier for representing field and method descriptors (e.g.
  308.      * {@code out:java.lang.PrintStream}) in a way that is compatible with passing String arrays.
  309.      *
  310.      * @param in the input stream to read from
  311.      * @throws IOException      if a problem occurs during reading from the underlying stream
  312.      * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported codec
  313.      */
  314.     private void parseCpDescriptor(final InputStream in) throws IOException, Pack200Exception {
  315.         final int cpDescriptorCount = header.getCpDescriptorCount();
  316.         cpDescriptorNameInts = decodeBandInt("cp_Descr_name", in, Codec.DELTA5, cpDescriptorCount);
  317.         cpDescriptorTypeInts = decodeBandInt("cp_Descr_type", in, Codec.UDELTA5, cpDescriptorCount);
  318.         final String[] cpDescriptorNames = getReferences(cpDescriptorNameInts, cpUTF8);
  319.         final String[] cpDescriptorTypes = getReferences(cpDescriptorTypeInts, cpSignature);
  320.         cpDescriptor = new String[cpDescriptorCount];
  321.         mapDescriptor = new HashMap<>(cpDescriptorCount);
  322.         for (int i = 0; i < cpDescriptorCount; i++) {
  323.             cpDescriptor[i] = cpDescriptorNames[i] + ":" + cpDescriptorTypes[i]; //$NON-NLS-1$
  324.             mapDescriptor.put(cpDescriptor[i], Integer.valueOf(i));
  325.         }
  326.     }

  327.     private void parseCpDouble(final InputStream in) throws IOException, Pack200Exception {
  328.         final int cpDoubleCount = header.getCpDoubleCount();
  329.         final long[] band = parseFlags("cp_Double", in, cpDoubleCount, Codec.UDELTA5, Codec.DELTA5);
  330.         cpDouble = new double[band.length];
  331.         Arrays.setAll(cpDouble, i -> Double.longBitsToDouble(band[i]));
  332.     }

  333.     /**
  334.      * Parses the constant pool field definitions, using {@link #cpFieldCount} to populate {@link #cpFieldClass} and {@link #cpFieldDescriptor}.
  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 parseCpField(final InputStream in) throws IOException, Pack200Exception {
  341.         final int cpFieldCount = header.getCpFieldCount();
  342.         cpFieldClassInts = decodeBandInt("cp_Field_class", in, Codec.DELTA5, cpFieldCount);
  343.         cpFieldDescriptorInts = decodeBandInt("cp_Field_desc", in, Codec.UDELTA5, cpFieldCount);
  344.         cpFieldClass = new String[cpFieldCount];
  345.         cpFieldDescriptor = new String[cpFieldCount];
  346.         for (int i = 0; i < cpFieldCount; i++) {
  347.             cpFieldClass[i] = cpClass[cpFieldClassInts[i]];
  348.             cpFieldDescriptor[i] = cpDescriptor[cpFieldDescriptorInts[i]];
  349.         }
  350.     }

  351.     private void parseCpFloat(final InputStream in) throws IOException, Pack200Exception {
  352.         final int cpFloatCount = header.getCpFloatCount();
  353.         final int[] floatBits = decodeBandInt("cp_Float", in, Codec.UDELTA5, cpFloatCount);
  354.         cpFloat = new float[cpFloatCount];
  355.         for (int i = 0; i < cpFloatCount; i++) {
  356.             cpFloat[i] = Float.intBitsToFloat(floatBits[i]);
  357.         }
  358.     }

  359.     /**
  360.      * Parses the constant pool interface method definitions, using {@link #cpIMethodCount} to populate {@link #cpIMethodClass} and
  361.      * {@link #cpIMethodDescriptor}.
  362.      *
  363.      * @param in the input stream to read from
  364.      * @throws IOException      if a problem occurs during reading from the underlying stream
  365.      * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported codec
  366.      */
  367.     private void parseCpIMethod(final InputStream in) throws IOException, Pack200Exception {
  368.         final int cpIMethodCount = header.getCpIMethodCount();
  369.         cpIMethodClassInts = decodeBandInt("cp_Imethod_class", in, Codec.DELTA5, cpIMethodCount);
  370.         cpIMethodDescriptorInts = decodeBandInt("cp_Imethod_desc", in, Codec.UDELTA5, cpIMethodCount);
  371.         cpIMethodClass = new String[cpIMethodCount];
  372.         cpIMethodDescriptor = new String[cpIMethodCount];
  373.         for (int i = 0; i < cpIMethodCount; i++) {
  374.             cpIMethodClass[i] = cpClass[cpIMethodClassInts[i]];
  375.             cpIMethodDescriptor[i] = cpDescriptor[cpIMethodDescriptorInts[i]];
  376.         }
  377.     }

  378.     private void parseCpInt(final InputStream in) throws IOException, Pack200Exception {
  379.         final int cpIntCount = header.getCpIntCount();
  380.         cpInt = decodeBandInt("cpInt", in, Codec.UDELTA5, cpIntCount);
  381.     }

  382.     private void parseCpLong(final InputStream in) throws IOException, Pack200Exception {
  383.         final int cpLongCount = header.getCpLongCount();
  384.         cpLong = parseFlags("cp_Long", in, cpLongCount, Codec.UDELTA5, Codec.DELTA5);
  385.     }

  386.     /**
  387.      * Parses the constant pool method definitions, using {@link #cpMethodCount} to populate {@link #cpMethodClass} and {@link #cpMethodDescriptor}.
  388.      *
  389.      * @param in the input stream to read from
  390.      * @throws IOException      if a problem occurs during reading from the underlying stream
  391.      * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported codec
  392.      */
  393.     private void parseCpMethod(final InputStream in) throws IOException, Pack200Exception {
  394.         final int cpMethodCount = header.getCpMethodCount();
  395.         cpMethodClassInts = decodeBandInt("cp_Method_class", in, Codec.DELTA5, cpMethodCount);
  396.         cpMethodDescriptorInts = decodeBandInt("cp_Method_desc", in, Codec.UDELTA5, cpMethodCount);
  397.         cpMethodClass = new String[cpMethodCount];
  398.         cpMethodDescriptor = new String[cpMethodCount];
  399.         for (int i = 0; i < cpMethodCount; i++) {
  400.             cpMethodClass[i] = cpClass[cpMethodClassInts[i]];
  401.             cpMethodDescriptor[i] = cpDescriptor[cpMethodDescriptorInts[i]];
  402.         }
  403.     }

  404.     /**
  405.      * Parses the constant pool signature classes, using {@link #cpSignatureCount} to populate {@link #cpSignature}. A signature form is akin to the bytecode
  406.      * 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
  407.      * 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
  408.      * {@code [L(V)} and a classes array of {@code [java.lang.String]}. The {@link #cpSignature} is a string representation identical to the bytecode equivalent
  409.      * {@code [Ljava/lang/String;(V)} TODO Check that the form is as above and update other types e.g. J
  410.      *
  411.      * @param in the input stream to read from
  412.      * @throws IOException      if a problem occurs during reading from the underlying stream
  413.      * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported codec
  414.      */
  415.     private void parseCpSignature(final InputStream in) throws IOException, Pack200Exception {
  416.         final int cpSignatureCount = header.getCpSignatureCount();
  417.         cpSignatureInts = decodeBandInt("cp_Signature_form", in, Codec.DELTA5, cpSignatureCount);
  418.         final String[] cpSignatureForm = getReferences(cpSignatureInts, cpUTF8);
  419.         cpSignature = new String[cpSignatureCount];
  420.         mapSignature = new HashMap<>();
  421.         int lCount = 0;
  422.         for (int i = 0; i < cpSignatureCount; i++) {
  423.             final String form = cpSignatureForm[i];
  424.             final char[] chars = form.toCharArray();
  425.             for (final char element : chars) {
  426.                 if (element == 'L') {
  427.                     cpSignatureInts[i] = -1;
  428.                     lCount++;
  429.                 }
  430.             }
  431.         }
  432.         final String[] cpSignatureClasses = parseReferences("cp_Signature_classes", in, Codec.UDELTA5, lCount, cpClass);
  433.         int index = 0;
  434.         for (int i = 0; i < cpSignatureCount; i++) {
  435.             final String form = cpSignatureForm[i];
  436.             final int len = form.length();
  437.             final StringBuilder signature = new StringBuilder(64);
  438.             for (int j = 0; j < len; j++) {
  439.                 final char c = form.charAt(j);
  440.                 signature.append(c);
  441.                 if (c == 'L') {
  442.                     final String className = cpSignatureClasses[index];
  443.                     signature.append(className);
  444.                     index++;
  445.                 }
  446.             }
  447.             cpSignature[i] = signature.toString();
  448.             mapSignature.put(signature.toString(), Integer.valueOf(i));
  449.         }
  450. //        for (int i = 0; i < cpSignatureInts.length; i++) {
  451. //            if (cpSignatureInts[i] == -1) {
  452. //                cpSignatureInts[i] = search(cpUTF8, cpSignature[i]);
  453. //            }
  454. //        }
  455.     }

  456.     /**
  457.      * Parses the constant pool strings, using {@link #cpStringCount} to populate {@link #cpString} from indexes into {@link #cpUTF8}.
  458.      *
  459.      * @param in the input stream to read from
  460.      * @throws IOException      if a problem occurs during reading from the underlying stream
  461.      * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported codec
  462.      */
  463.     private void parseCpString(final InputStream in) throws IOException, Pack200Exception {
  464.         final int cpStringCount = header.getCpStringCount();
  465.         cpStringInts = decodeBandInt("cp_String", in, Codec.UDELTA5, cpStringCount);
  466.         cpString = new String[cpStringCount];
  467.         Arrays.setAll(cpString, i -> cpUTF8[cpStringInts[i]]);
  468.     }

  469.     private void parseCpUtf8(final InputStream in) throws IOException, Pack200Exception {
  470.         final int cpUTF8Count = header.getCpUTF8Count();
  471.         if (cpUTF8Count <= 0) {
  472.             throw new IOException("cpUTF8Count value must be greater than 0");
  473.         }
  474.         final int[] prefix = decodeBandInt("cpUTF8Prefix", in, Codec.DELTA5, cpUTF8Count - 2);
  475.         int charCount = 0;
  476.         int bigSuffixCount = 0;
  477.         final int[] suffix = decodeBandInt("cpUTF8Suffix", in, Codec.UNSIGNED5, cpUTF8Count - 1);

  478.         for (final int element : suffix) {
  479.             if (element == 0) {
  480.                 bigSuffixCount++;
  481.             } else {
  482.                 charCount += element;
  483.             }
  484.         }
  485.         final int[] dataBand = decodeBandInt("cp_Utf8_chars", in, Codec.CHAR3, charCount);
  486.         final char[] data = new char[charCount];
  487.         for (int i = 0; i < data.length; i++) {
  488.             data[i] = (char) dataBand[i];
  489.         }

  490.         // Read in the big suffix data
  491.         final int[] bigSuffixCounts = decodeBandInt("cp_Utf8_big_suffix", in, Codec.DELTA5, bigSuffixCount);
  492.         final int[][] bigSuffixDataBand = new int[bigSuffixCount][];
  493.         for (int i = 0; i < bigSuffixDataBand.length; i++) {
  494.             bigSuffixDataBand[i] = decodeBandInt("cp_Utf8_big_chars " + i, in, Codec.DELTA5, bigSuffixCounts[i]);
  495.         }

  496.         // Convert big suffix data to characters
  497.         final char[][] bigSuffixData = new char[bigSuffixCount][];
  498.         for (int i = 0; i < bigSuffixDataBand.length; i++) {
  499.             bigSuffixData[i] = new char[bigSuffixDataBand[i].length];
  500.             for (int j = 0; j < bigSuffixDataBand[i].length; j++) {
  501.                 bigSuffixData[i][j] = (char) bigSuffixDataBand[i][j];
  502.             }
  503.         }

  504.         // Initialize variables
  505.         mapUTF8 = new HashMap<>(cpUTF8Count + 1);
  506.         cpUTF8 = new String[cpUTF8Count];
  507.         cpUTF8[0] = EMPTY_STRING;
  508.         mapUTF8.put(EMPTY_STRING, Integer.valueOf(0));

  509.         // Go through the strings
  510.         charCount = 0;
  511.         bigSuffixCount = 0;
  512.         for (int i = 1; i < cpUTF8Count; i++) {
  513.             final String lastString = cpUTF8[i - 1];
  514.             if (suffix[i - 1] == 0) {
  515.                 // The big suffix stuff hasn't been tested, and I'll be
  516.                 // surprised if it works first time w/o errors ...
  517.                 cpUTF8[i] = lastString.substring(0, i > 1 ? prefix[i - 2] : 0) + new String(bigSuffixData[bigSuffixCount++]);
  518.                 mapUTF8.put(cpUTF8[i], Integer.valueOf(i));
  519.             } else {
  520.                 cpUTF8[i] = lastString.substring(0, i > 1 ? prefix[i - 2] : 0) + new String(data, charCount, suffix[i - 1]);
  521.                 charCount += suffix[i - 1];
  522.                 mapUTF8.put(cpUTF8[i], Integer.valueOf(i));
  523.             }
  524.         }
  525.     }

  526.     @Override
  527.     public void read(final InputStream in) throws IOException, Pack200Exception {
  528.         parseCpUtf8(in);
  529.         parseCpInt(in);
  530.         parseCpFloat(in);
  531.         parseCpLong(in);
  532.         parseCpDouble(in);
  533.         parseCpString(in);
  534.         parseCpClass(in);
  535.         parseCpSignature(in);
  536.         parseCpDescriptor(in);
  537.         parseCpField(in);
  538.         parseCpMethod(in);
  539.         parseCpIMethod(in);

  540.         intOffset = cpUTF8.length;
  541.         floatOffset = intOffset + cpInt.length;
  542.         longOffset = floatOffset + cpFloat.length;
  543.         doubleOffset = longOffset + cpLong.length;
  544.         stringOffset = doubleOffset + cpDouble.length;
  545.         classOffset = stringOffset + cpString.length;
  546.         signatureOffset = classOffset + cpClass.length;
  547.         descrOffset = signatureOffset + cpSignature.length;
  548.         fieldOffset = descrOffset + cpDescriptor.length;
  549.         methodOffset = fieldOffset + cpFieldClass.length;
  550.         imethodOffset = methodOffset + cpMethodClass.length;
  551.     }

  552.     @Override
  553.     public void unpack() {

  554.     }

  555. }