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 this
  4.  * work for additional information regarding copyright ownership. The ASF
  5.  * licenses this file to You under the Apache License, Version 2.0 (the
  6.  * "License"); you may not use this file except in compliance with the License.
  7.  * 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, WITHOUT
  13.  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  14.  * License for the specific language governing permissions and limitations under
  15.  * the License.
  16.  */
  17. package org.apache.commons.compress.harmony.pack200;

  18. import java.io.IOException;
  19. import java.io.OutputStream;
  20. import java.util.ArrayList;
  21. import java.util.Arrays;
  22. import java.util.HashMap;
  23. import java.util.HashSet;
  24. import java.util.List;
  25. import java.util.Map;
  26. import java.util.Set;
  27. import java.util.TreeSet;

  28. import org.objectweb.asm.Type;

  29. /**
  30.  * Pack200 Constant Pool Bands
  31.  */
  32. public class CpBands extends BandSet {

  33.     // Don't need to include default attribute names in the constant pool bands
  34.     private final Set<String> defaultAttributeNames = new HashSet<>();

  35.     private final Set<CPUTF8> cp_Utf8 = new TreeSet<>();
  36.     private final Set<CPInt> cp_Int = new TreeSet<>();
  37.     private final Set<CPFloat> cp_Float = new TreeSet<>();
  38.     private final Set<CPLong> cp_Long = new TreeSet<>();
  39.     private final Set<CPDouble> cp_Double = new TreeSet<>();
  40.     private final Set<CPString> cp_String = new TreeSet<>();
  41.     private final Set<CPClass> cp_Class = new TreeSet<>();
  42.     private final Set<CPSignature> cp_Signature = new TreeSet<>();
  43.     private final Set<CPNameAndType> cp_Descr = new TreeSet<>();
  44.     private final Set<CPMethodOrField> cp_Field = new TreeSet<>();
  45.     private final Set<CPMethodOrField> cp_Method = new TreeSet<>();
  46.     private final Set<CPMethodOrField> cp_Imethod = new TreeSet<>();

  47.     private final Map<String, CPUTF8> stringsToCpUtf8 = new HashMap<>();
  48.     private final Map<String, CPNameAndType> stringsToCpNameAndType = new HashMap<>();
  49.     private final Map<String, CPClass> stringsToCpClass = new HashMap<>();
  50.     private final Map<String, CPSignature> stringsToCpSignature = new HashMap<>();
  51.     private final Map<String, CPMethodOrField> stringsToCpMethod = new HashMap<>();
  52.     private final Map<String, CPMethodOrField> stringsToCpField = new HashMap<>();
  53.     private final Map<String, CPMethodOrField> stringsToCpIMethod = new HashMap<>();

  54.     private final Map<Object, CPConstant<?>> objectsToCPConstant = new HashMap<>();

  55.     private final Segment segment;

  56.     public CpBands(final Segment segment, final int effort) {
  57.         super(effort, segment.getSegmentHeader());
  58.         this.segment = segment;
  59.         defaultAttributeNames.add("AnnotationDefault");
  60.         defaultAttributeNames.add("RuntimeVisibleAnnotations");
  61.         defaultAttributeNames.add("RuntimeInvisibleAnnotations");
  62.         defaultAttributeNames.add("RuntimeVisibleParameterAnnotations");
  63.         defaultAttributeNames.add("RuntimeInvisibleParameterAnnotations");
  64.         defaultAttributeNames.add("Code");
  65.         defaultAttributeNames.add("LineNumberTable");
  66.         defaultAttributeNames.add("LocalVariableTable");
  67.         defaultAttributeNames.add("LocalVariableTypeTable");
  68.         defaultAttributeNames.add("ConstantValue");
  69.         defaultAttributeNames.add("Deprecated");
  70.         defaultAttributeNames.add("EnclosingMethod");
  71.         defaultAttributeNames.add("Exceptions");
  72.         defaultAttributeNames.add("InnerClasses");
  73.         defaultAttributeNames.add("Signature");
  74.         defaultAttributeNames.add("SourceFile");
  75.     }

  76.     private void addCharacters(final List<Character> chars, final char[] charArray) {
  77.         for (final char element : charArray) {
  78.             chars.add(Character.valueOf(element));
  79.         }
  80.     }

  81.     public void addCPClass(final String className) {
  82.         getCPClass(className);
  83.     }

  84.     void addCPUtf8(final String utf8) {
  85.         getCPUtf8(utf8);
  86.     }

  87.     private void addIndices() {
  88.         for (final Set<? extends ConstantPoolEntry> set : Arrays.asList(cp_Utf8, cp_Int, cp_Float, cp_Long, cp_Double, cp_String, cp_Class, cp_Signature,
  89.                 cp_Descr, cp_Field, cp_Method, cp_Imethod)) {
  90.             int j = 0;
  91.             for (final ConstantPoolEntry entry : set) {
  92.                 entry.setIndex(j);
  93.                 j++;
  94.             }
  95.         }
  96.         final Map<CPClass, Integer> classNameToIndex = new HashMap<>();
  97.         cp_Field.forEach(mOrF -> {
  98.             final CPClass cpClassName = mOrF.getClassName();
  99.             final Integer index = classNameToIndex.get(cpClassName);
  100.             if (index == null) {
  101.                 classNameToIndex.put(cpClassName, Integer.valueOf(1));
  102.                 mOrF.setIndexInClass(0);
  103.             } else {
  104.                 final int theIndex = index.intValue();
  105.                 mOrF.setIndexInClass(theIndex);
  106.                 classNameToIndex.put(cpClassName, Integer.valueOf(theIndex + 1));
  107.             }
  108.         });
  109.         classNameToIndex.clear();
  110.         final Map<CPClass, Integer> classNameToConstructorIndex = new HashMap<>();
  111.         cp_Method.forEach(mOrF -> {
  112.             final CPClass cpClassName = mOrF.getClassName();
  113.             final Integer index = classNameToIndex.get(cpClassName);
  114.             if (index == null) {
  115.                 classNameToIndex.put(cpClassName, Integer.valueOf(1));
  116.                 mOrF.setIndexInClass(0);
  117.             } else {
  118.                 final int theIndex = index.intValue();
  119.                 mOrF.setIndexInClass(theIndex);
  120.                 classNameToIndex.put(cpClassName, Integer.valueOf(theIndex + 1));
  121.             }
  122.             if (mOrF.getDesc().getName().equals("<init>")) {
  123.                 final Integer constructorIndex = classNameToConstructorIndex.get(cpClassName);
  124.                 if (constructorIndex == null) {
  125.                     classNameToConstructorIndex.put(cpClassName, Integer.valueOf(1));
  126.                     mOrF.setIndexInClassForConstructor(0);
  127.                 } else {
  128.                     final int theIndex = constructorIndex.intValue();
  129.                     mOrF.setIndexInClassForConstructor(theIndex);
  130.                     classNameToConstructorIndex.put(cpClassName, Integer.valueOf(theIndex + 1));
  131.                 }
  132.             }
  133.         });
  134.     }

  135.     public boolean existsCpClass(final String className) {
  136.         return stringsToCpClass.containsKey(className);
  137.     }

  138.     /**
  139.      * All input classes for the segment have now been read in, so this method is called so that this class can calculate/complete anything it could not do
  140.      * while classes were being read.
  141.      */
  142.     public void finaliseBands() {
  143.         addCPUtf8("");
  144.         removeSignaturesFromCpUTF8();
  145.         addIndices();
  146.         segmentHeader.setCp_Utf8_count(cp_Utf8.size());
  147.         segmentHeader.setCp_Int_count(cp_Int.size());
  148.         segmentHeader.setCp_Float_count(cp_Float.size());
  149.         segmentHeader.setCp_Long_count(cp_Long.size());
  150.         segmentHeader.setCp_Double_count(cp_Double.size());
  151.         segmentHeader.setCp_String_count(cp_String.size());
  152.         segmentHeader.setCp_Class_count(cp_Class.size());
  153.         segmentHeader.setCp_Signature_count(cp_Signature.size());
  154.         segmentHeader.setCp_Descr_count(cp_Descr.size());
  155.         segmentHeader.setCp_Field_count(cp_Field.size());
  156.         segmentHeader.setCp_Method_count(cp_Method.size());
  157.         segmentHeader.setCp_Imethod_count(cp_Imethod.size());
  158.     }

  159.     public CPConstant<?> getConstant(final Object value) {
  160.         CPConstant<?> constant = objectsToCPConstant.get(value);
  161.         if (constant == null) {
  162.             if (value instanceof Integer) {
  163.                 constant = new CPInt(((Integer) value).intValue());
  164.                 cp_Int.add((CPInt) constant);
  165.             } else if (value instanceof Long) {
  166.                 constant = new CPLong(((Long) value).longValue());
  167.                 cp_Long.add((CPLong) constant);
  168.             } else if (value instanceof Float) {
  169.                 constant = new CPFloat(((Float) value).floatValue());
  170.                 cp_Float.add((CPFloat) constant);
  171.             } else if (value instanceof Double) {
  172.                 constant = new CPDouble(((Double) value).doubleValue());
  173.                 cp_Double.add((CPDouble) constant);
  174.             } else if (value instanceof String) {
  175.                 constant = new CPString(getCPUtf8((String) value));
  176.                 cp_String.add((CPString) constant);
  177.             } else if (value instanceof Type) {
  178.                 String className = ((Type) value).getClassName();
  179.                 if (className.endsWith("[]")) {
  180.                     className = "[L" + className.substring(0, className.length() - 2);
  181.                     while (className.endsWith("[]")) {
  182.                         className = "[" + className.substring(0, className.length() - 2);
  183.                     }
  184.                     className += ";";
  185.                 }
  186.                 constant = getCPClass(className);
  187.             }
  188.             objectsToCPConstant.put(value, constant);
  189.         }
  190.         return constant;
  191.     }

  192.     public CPClass getCPClass(String className) {
  193.         if (className == null) {
  194.             return null;
  195.         }
  196.         className = className.replace('.', '/');
  197.         CPClass cpClass = stringsToCpClass.get(className);
  198.         if (cpClass == null) {
  199.             final CPUTF8 cpUtf8 = getCPUtf8(className);
  200.             cpClass = new CPClass(cpUtf8);
  201.             cp_Class.add(cpClass);
  202.             stringsToCpClass.put(className, cpClass);
  203.         }
  204.         if (cpClass.isInnerClass()) {
  205.             segment.getClassBands().currentClassReferencesInnerClass(cpClass);
  206.         }
  207.         return cpClass;
  208.     }

  209.     public CPMethodOrField getCPField(final CPClass cpClass, final String name, final String desc) {
  210.         final String key = cpClass.toString() + ":" + name + ":" + desc;
  211.         CPMethodOrField cpF = stringsToCpField.get(key);
  212.         if (cpF == null) {
  213.             final CPNameAndType nAndT = getCPNameAndType(name, desc);
  214.             cpF = new CPMethodOrField(cpClass, nAndT);
  215.             cp_Field.add(cpF);
  216.             stringsToCpField.put(key, cpF);
  217.         }
  218.         return cpF;
  219.     }

  220.     public CPMethodOrField getCPField(final String owner, final String name, final String desc) {
  221.         return getCPField(getCPClass(owner), name, desc);
  222.     }

  223.     public CPMethodOrField getCPIMethod(final CPClass cpClass, final String name, final String desc) {
  224.         final String key = cpClass.toString() + ":" + name + ":" + desc;
  225.         CPMethodOrField cpIM = stringsToCpIMethod.get(key);
  226.         if (cpIM == null) {
  227.             final CPNameAndType nAndT = getCPNameAndType(name, desc);
  228.             cpIM = new CPMethodOrField(cpClass, nAndT);
  229.             cp_Imethod.add(cpIM);
  230.             stringsToCpIMethod.put(key, cpIM);
  231.         }
  232.         return cpIM;
  233.     }

  234.     public CPMethodOrField getCPIMethod(final String owner, final String name, final String desc) {
  235.         return getCPIMethod(getCPClass(owner), name, desc);
  236.     }

  237.     public CPMethodOrField getCPMethod(final CPClass cpClass, final String name, final String desc) {
  238.         final String key = cpClass.toString() + ":" + name + ":" + desc;
  239.         CPMethodOrField cpM = stringsToCpMethod.get(key);
  240.         if (cpM == null) {
  241.             final CPNameAndType nAndT = getCPNameAndType(name, desc);
  242.             cpM = new CPMethodOrField(cpClass, nAndT);
  243.             cp_Method.add(cpM);
  244.             stringsToCpMethod.put(key, cpM);
  245.         }
  246.         return cpM;
  247.     }

  248.     public CPMethodOrField getCPMethod(final String owner, final String name, final String desc) {
  249.         return getCPMethod(getCPClass(owner), name, desc);
  250.     }

  251.     public CPNameAndType getCPNameAndType(final String name, final String signature) {
  252.         final String descr = name + ":" + signature;
  253.         CPNameAndType nameAndType = stringsToCpNameAndType.get(descr);
  254.         if (nameAndType == null) {
  255.             nameAndType = new CPNameAndType(getCPUtf8(name), getCPSignature(signature));
  256.             stringsToCpNameAndType.put(descr, nameAndType);
  257.             cp_Descr.add(nameAndType);
  258.         }
  259.         return nameAndType;
  260.     }

  261.     public CPSignature getCPSignature(final String signature) {
  262.         if (signature == null) {
  263.             return null;
  264.         }
  265.         CPSignature cpS = stringsToCpSignature.get(signature);
  266.         if (cpS == null) {
  267.             final List<CPClass> cpClasses = new ArrayList<>();
  268.             CPUTF8 signatureUTF8;
  269.             if (signature.length() > 1 && signature.indexOf('L') != -1) {
  270.                 final List<String> classes = new ArrayList<>();
  271.                 final char[] chars = signature.toCharArray();
  272.                 final StringBuilder signatureString = new StringBuilder();
  273.                 for (int i = 0; i < chars.length; i++) {
  274.                     signatureString.append(chars[i]);
  275.                     if (chars[i] == 'L') {
  276.                         final StringBuilder className = new StringBuilder();
  277.                         for (int j = i + 1; j < chars.length; j++) {
  278.                             final char c = chars[j];
  279.                             if (!Character.isLetter(c) && !Character.isDigit(c) && c != '/' && c != '$' && c != '_') {
  280.                                 classes.add(className.toString());
  281.                                 i = j - 1;
  282.                                 break;
  283.                             }
  284.                             className.append(c);
  285.                         }
  286.                     }
  287.                 }
  288.                 removeCpUtf8(signature);
  289.                 for (String className : classes) {
  290.                     CPClass cpClass = null;
  291.                     if (className != null) {
  292.                         className = className.replace('.', '/');
  293.                         cpClass = stringsToCpClass.get(className);
  294.                         if (cpClass == null) {
  295.                             final CPUTF8 cpUtf8 = getCPUtf8(className);
  296.                             cpClass = new CPClass(cpUtf8);
  297.                             cp_Class.add(cpClass);
  298.                             stringsToCpClass.put(className, cpClass);
  299.                         }
  300.                     }
  301.                     cpClasses.add(cpClass);
  302.                 }

  303.                 signatureUTF8 = getCPUtf8(signatureString.toString());
  304.             } else {
  305.                 signatureUTF8 = getCPUtf8(signature);
  306.             }
  307.             cpS = new CPSignature(signature, signatureUTF8, cpClasses);
  308.             cp_Signature.add(cpS);
  309.             stringsToCpSignature.put(signature, cpS);
  310.         }
  311.         return cpS;
  312.     }

  313.     public CPUTF8 getCPUtf8(final String utf8) {
  314.         if (utf8 == null) {
  315.             return null;
  316.         }
  317.         CPUTF8 cpUtf8 = stringsToCpUtf8.get(utf8);
  318.         if (cpUtf8 == null) {
  319.             cpUtf8 = new CPUTF8(utf8);
  320.             cp_Utf8.add(cpUtf8);
  321.             stringsToCpUtf8.put(utf8, cpUtf8);
  322.         }
  323.         return cpUtf8;
  324.     }

  325.     @Override
  326.     public void pack(final OutputStream out) throws IOException, Pack200Exception {
  327.         PackingUtils.log("Writing constant pool bands...");
  328.         writeCpUtf8(out);
  329.         writeCpInt(out);
  330.         writeCpFloat(out);
  331.         writeCpLong(out);
  332.         writeCpDouble(out);
  333.         writeCpString(out);
  334.         writeCpClass(out);
  335.         writeCpSignature(out);
  336.         writeCpDescr(out);
  337.         writeCpMethodOrField(cp_Field, out, "cp_Field");
  338.         writeCpMethodOrField(cp_Method, out, "cp_Method");
  339.         writeCpMethodOrField(cp_Imethod, out, "cp_Imethod");
  340.     }

  341.     private void removeCpUtf8(final String string) {
  342.         final CPUTF8 utf8 = stringsToCpUtf8.get(string);
  343.         if (utf8 != null && stringsToCpClass.get(string) == null) { // don't remove if strings are also in cpclass
  344.             stringsToCpUtf8.remove(string);
  345.             cp_Utf8.remove(utf8);
  346.         }
  347.     }

  348.     private void removeSignaturesFromCpUTF8() {
  349.         cp_Signature.forEach(signature -> {
  350.             final String sigStr = signature.getUnderlyingString();
  351.             final CPUTF8 utf8 = signature.getSignatureForm();
  352.             final String form = utf8.getUnderlyingString();
  353.             if (!sigStr.equals(form)) {
  354.                 removeCpUtf8(sigStr);
  355.             }
  356.         });
  357.     }

  358.     private void writeCpClass(final OutputStream out) throws IOException, Pack200Exception {
  359.         PackingUtils.log("Writing " + cp_Class.size() + " Class entries...");
  360.         final int[] cpClass = new int[cp_Class.size()];
  361.         int i = 0;
  362.         for (final CPClass cpCl : cp_Class) {
  363.             cpClass[i] = cpCl.getIndexInCpUtf8();
  364.             i++;
  365.         }
  366.         final byte[] encodedBand = encodeBandInt("cpClass", cpClass, Codec.UDELTA5);
  367.         out.write(encodedBand);
  368.         PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpClass[" + cpClass.length + "]");
  369.     }

  370.     private void writeCpDescr(final OutputStream out) throws IOException, Pack200Exception {
  371.         PackingUtils.log("Writing " + cp_Descr.size() + " Descriptor entries...");
  372.         final int[] cpDescrName = new int[cp_Descr.size()];
  373.         final int[] cpDescrType = new int[cp_Descr.size()];
  374.         int i = 0;
  375.         for (final CPNameAndType nameAndType : cp_Descr) {
  376.             cpDescrName[i] = nameAndType.getNameIndex();
  377.             cpDescrType[i] = nameAndType.getTypeIndex();
  378.             i++;
  379.         }

  380.         byte[] encodedBand = encodeBandInt("cp_Descr_Name", cpDescrName, Codec.DELTA5);
  381.         out.write(encodedBand);
  382.         PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Descr_Name[" + cpDescrName.length + "]");

  383.         encodedBand = encodeBandInt("cp_Descr_Type", cpDescrType, Codec.UDELTA5);
  384.         out.write(encodedBand);
  385.         PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Descr_Type[" + cpDescrType.length + "]");
  386.     }

  387.     private void writeCpDouble(final OutputStream out) throws IOException, Pack200Exception {
  388.         PackingUtils.log("Writing " + cp_Double.size() + " Double entries...");
  389.         final int[] highBits = new int[cp_Double.size()];
  390.         final int[] loBits = new int[cp_Double.size()];
  391.         int i = 0;
  392.         for (final CPDouble dbl : cp_Double) {
  393.             final long l = Double.doubleToLongBits(dbl.getDouble());
  394.             highBits[i] = (int) (l >> 32);
  395.             loBits[i] = (int) l;
  396.             i++;
  397.         }
  398.         byte[] encodedBand = encodeBandInt("cp_Double_hi", highBits, Codec.UDELTA5);
  399.         out.write(encodedBand);
  400.         PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Double_hi[" + highBits.length + "]");

  401.         encodedBand = encodeBandInt("cp_Double_lo", loBits, Codec.DELTA5);
  402.         out.write(encodedBand);
  403.         PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Double_lo[" + loBits.length + "]");
  404.     }

  405.     private void writeCpFloat(final OutputStream out) throws IOException, Pack200Exception {
  406.         PackingUtils.log("Writing " + cp_Float.size() + " Float entries...");
  407.         final int[] cpFloat = new int[cp_Float.size()];
  408.         int i = 0;
  409.         for (final CPFloat fl : cp_Float) {
  410.             cpFloat[i] = Float.floatToIntBits(fl.getFloat());
  411.             i++;
  412.         }
  413.         final byte[] encodedBand = encodeBandInt("cp_Float", cpFloat, Codec.UDELTA5);
  414.         out.write(encodedBand);
  415.         PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Float[" + cpFloat.length + "]");
  416.     }

  417.     private void writeCpInt(final OutputStream out) throws IOException, Pack200Exception {
  418.         PackingUtils.log("Writing " + cp_Int.size() + " Integer entries...");
  419.         final int[] cpInt = new int[cp_Int.size()];
  420.         int i = 0;
  421.         for (final CPInt integer : cp_Int) {
  422.             cpInt[i] = integer.getInt();
  423.             i++;
  424.         }
  425.         final byte[] encodedBand = encodeBandInt("cp_Int", cpInt, Codec.UDELTA5);
  426.         out.write(encodedBand);
  427.         PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Int[" + cpInt.length + "]");
  428.     }

  429.     private void writeCpLong(final OutputStream out) throws IOException, Pack200Exception {
  430.         PackingUtils.log("Writing " + cp_Long.size() + " Long entries...");
  431.         final int[] highBits = new int[cp_Long.size()];
  432.         final int[] loBits = new int[cp_Long.size()];
  433.         int i = 0;
  434.         for (final CPLong lng : cp_Long) {
  435.             final long l = lng.getLong();
  436.             highBits[i] = (int) (l >> 32);
  437.             loBits[i] = (int) l;
  438.             i++;
  439.         }
  440.         byte[] encodedBand = encodeBandInt("cp_Long_hi", highBits, Codec.UDELTA5);
  441.         out.write(encodedBand);
  442.         PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Long_hi[" + highBits.length + "]");

  443.         encodedBand = encodeBandInt("cp_Long_lo", loBits, Codec.DELTA5);
  444.         out.write(encodedBand);
  445.         PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Long_lo[" + loBits.length + "]");
  446.     }

  447.     private void writeCpMethodOrField(final Set<CPMethodOrField> cp, final OutputStream out, final String name) throws IOException, Pack200Exception {
  448.         PackingUtils.log("Writing " + cp.size() + " Method and Field entries...");
  449.         final int[] cp_methodOrField_class = new int[cp.size()];
  450.         final int[] cp_methodOrField_desc = new int[cp.size()];
  451.         int i = 0;
  452.         for (final CPMethodOrField mOrF : cp) {
  453.             cp_methodOrField_class[i] = mOrF.getClassIndex();
  454.             cp_methodOrField_desc[i] = mOrF.getDescIndex();
  455.             i++;
  456.         }
  457.         byte[] encodedBand = encodeBandInt(name + "_class", cp_methodOrField_class, Codec.DELTA5);
  458.         out.write(encodedBand);
  459.         PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + name + "_class[" + cp_methodOrField_class.length + "]");

  460.         encodedBand = encodeBandInt(name + "_desc", cp_methodOrField_desc, Codec.UDELTA5);
  461.         out.write(encodedBand);
  462.         PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + name + "_desc[" + cp_methodOrField_desc.length + "]");
  463.     }

  464.     private void writeCpSignature(final OutputStream out) throws IOException, Pack200Exception {
  465.         PackingUtils.log("Writing " + cp_Signature.size() + " Signature entries...");
  466.         final int[] cpSignatureForm = new int[cp_Signature.size()];
  467.         final List<CPClass> classes = new ArrayList<>();
  468.         int i = 0;
  469.         for (final CPSignature cpS : cp_Signature) {
  470.             classes.addAll(cpS.getClasses());
  471.             cpSignatureForm[i] = cpS.getIndexInCpUtf8();
  472.             i++;
  473.         }
  474.         final int[] cpSignatureClasses = new int[classes.size()];
  475.         Arrays.setAll(cpSignatureClasses, j -> classes.get(j).getIndex());

  476.         byte[] encodedBand = encodeBandInt("cpSignatureForm", cpSignatureForm, Codec.DELTA5);
  477.         out.write(encodedBand);
  478.         PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpSignatureForm[" + cpSignatureForm.length + "]");

  479.         encodedBand = encodeBandInt("cpSignatureClasses", cpSignatureClasses, Codec.UDELTA5);
  480.         out.write(encodedBand);
  481.         PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpSignatureClasses[" + cpSignatureClasses.length + "]");
  482.     }

  483.     private void writeCpString(final OutputStream out) throws IOException, Pack200Exception {
  484.         PackingUtils.log("Writing " + cp_String.size() + " String entries...");
  485.         final int[] cpString = new int[cp_String.size()];
  486.         int i = 0;
  487.         for (final CPString cpStr : cp_String) {
  488.             cpString[i] = cpStr.getIndexInCpUtf8();
  489.             i++;
  490.         }
  491.         final byte[] encodedBand = encodeBandInt("cpString", cpString, Codec.UDELTA5);
  492.         out.write(encodedBand);
  493.         PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpString[" + cpString.length + "]");
  494.     }

  495.     private void writeCpUtf8(final OutputStream out) throws IOException, Pack200Exception {
  496.         PackingUtils.log("Writing " + cp_Utf8.size() + " UTF8 entries...");
  497.         final int[] cpUtf8Prefix = new int[cp_Utf8.size() - 2];
  498.         final int[] cpUtf8Suffix = new int[cp_Utf8.size() - 1];
  499.         final List<Character> chars = new ArrayList<>();
  500.         final List<Integer> bigSuffix = new ArrayList<>();
  501.         final List<Character> bigChars = new ArrayList<>();
  502.         final Object[] cpUtf8Array = cp_Utf8.toArray();
  503.         final String first = ((CPUTF8) cpUtf8Array[1]).getUnderlyingString();
  504.         cpUtf8Suffix[0] = first.length();
  505.         addCharacters(chars, first.toCharArray());
  506.         for (int i = 2; i < cpUtf8Array.length; i++) {
  507.             final char[] previous = ((CPUTF8) cpUtf8Array[i - 1]).getUnderlyingString().toCharArray();
  508.             String currentStr = ((CPUTF8) cpUtf8Array[i]).getUnderlyingString();
  509.             final char[] current = currentStr.toCharArray();
  510.             int prefix = 0;
  511.             for (int j = 0; j < previous.length; j++) {
  512.                 if (previous[j] != current[j]) {
  513.                     break;
  514.                 }
  515.                 prefix++;
  516.             }
  517.             cpUtf8Prefix[i - 2] = prefix;
  518.             currentStr = currentStr.substring(prefix);
  519.             final char[] suffix = currentStr.toCharArray();
  520.             if (suffix.length > 1000) { // big suffix (1000 is arbitrary - can we
  521.                 // do better?)
  522.                 cpUtf8Suffix[i - 1] = 0;
  523.                 bigSuffix.add(Integer.valueOf(suffix.length));
  524.                 addCharacters(bigChars, suffix);
  525.             } else {
  526.                 cpUtf8Suffix[i - 1] = suffix.length;
  527.                 addCharacters(chars, suffix);
  528.             }
  529.         }
  530.         final int[] cpUtf8Chars = new int[chars.size()];
  531.         final int[] cpUtf8BigSuffix = new int[bigSuffix.size()];
  532.         final int[][] cpUtf8BigChars = new int[bigSuffix.size()][];
  533.         Arrays.setAll(cpUtf8Chars, i -> chars.get(i).charValue());
  534.         for (int i = 0; i < cpUtf8BigSuffix.length; i++) {
  535.             final int numBigChars = bigSuffix.get(i).intValue();
  536.             cpUtf8BigSuffix[i] = numBigChars;
  537.             cpUtf8BigChars[i] = new int[numBigChars];
  538.             Arrays.setAll(cpUtf8BigChars[i], j -> bigChars.remove(0).charValue());
  539.         }

  540.         byte[] encodedBand = encodeBandInt("cpUtf8Prefix", cpUtf8Prefix, Codec.DELTA5);
  541.         out.write(encodedBand);
  542.         PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpUtf8Prefix[" + cpUtf8Prefix.length + "]");

  543.         encodedBand = encodeBandInt("cpUtf8Suffix", cpUtf8Suffix, Codec.UNSIGNED5);
  544.         out.write(encodedBand);
  545.         PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpUtf8Suffix[" + cpUtf8Suffix.length + "]");

  546.         encodedBand = encodeBandInt("cpUtf8Chars", cpUtf8Chars, Codec.CHAR3);
  547.         out.write(encodedBand);
  548.         PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpUtf8Chars[" + cpUtf8Chars.length + "]");

  549.         encodedBand = encodeBandInt("cpUtf8BigSuffix", cpUtf8BigSuffix, Codec.DELTA5);
  550.         out.write(encodedBand);
  551.         PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpUtf8BigSuffix[" + cpUtf8BigSuffix.length + "]");

  552.         for (int i = 0; i < cpUtf8BigChars.length; i++) {
  553.             encodedBand = encodeBandInt("cpUtf8BigChars " + i, cpUtf8BigChars[i], Codec.DELTA5);
  554.             out.write(encodedBand);
  555.             PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpUtf8BigChars" + i + "[" + cpUtf8BigChars[i].length + "]");
  556.         }
  557.     }

  558. }