    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements. See the NOTICE file distributed with this
    004 * work for additional information regarding copyright ownership. The ASF
    005 * licenses this file to You under the Apache License, Version 2.0 (the
    006 * "License"); you may not use this file except in compliance with the License.
    007 * You may obtain a copy of the License at
    008 *
    009 * http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    013 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    014 * License for the specific language governing permissions and limitations under
    015 * the License.
    016 */
    017package org.apache.commons.compress.harmony.pack200;
    018
    019import java.io.IOException;
    020import java.io.OutputStream;
    021import java.util.ArrayList;
    022import java.util.Arrays;
    023import java.util.HashMap;
    024import java.util.HashSet;
    025import java.util.List;
    026import java.util.Map;
    027import java.util.Set;
    028import java.util.TreeSet;
    029
    030import org.objectweb.asm.Type;
    031
    032/**
    033 * Pack200 Constant Pool Bands
    034 */
    035public class CpBands extends BandSet {
    036
    037    // Don't need to include default attribute names in the constant pool bands
    038    private final Set<String> defaultAttributeNames = new HashSet<>();
    039
    040    private final Set<CPUTF8> cp_Utf8 = new TreeSet<>();
    041    private final Set<CPInt> cp_Int = new TreeSet<>();
    042    private final Set<CPFloat> cp_Float = new TreeSet<>();
    043    private final Set<CPLong> cp_Long = new TreeSet<>();
    044    private final Set<CPDouble> cp_Double = new TreeSet<>();
    045    private final Set<CPString> cp_String = new TreeSet<>();
    046    private final Set<CPClass> cp_Class = new TreeSet<>();
    047    private final Set<CPSignature> cp_Signature = new TreeSet<>();
    048    private final Set<CPNameAndType> cp_Descr = new TreeSet<>();
    049    private final Set<CPMethodOrField> cp_Field = new TreeSet<>();
    050    private final Set<CPMethodOrField> cp_Method = new TreeSet<>();
    051    private final Set<CPMethodOrField> cp_Imethod = new TreeSet<>();
    052
    053    private final Map<String, CPUTF8> stringsToCpUtf8 = new HashMap<>();
    054    private final Map<String, CPNameAndType> stringsToCpNameAndType = new HashMap<>();
    055    private final Map<String, CPClass> stringsToCpClass = new HashMap<>();
    056    private final Map<String, CPSignature> stringsToCpSignature = new HashMap<>();
    057    private final Map<String, CPMethodOrField> stringsToCpMethod = new HashMap<>();
    058    private final Map<String, CPMethodOrField> stringsToCpField = new HashMap<>();
    059    private final Map<String, CPMethodOrField> stringsToCpIMethod = new HashMap<>();
    060
    061    private final Map<Object, CPConstant<?>> objectsToCPConstant = new HashMap<>();
    062
    063    private final Segment segment;
    064
    065    public CpBands(final Segment segment, final int effort) {
    066        super(effort, segment.getSegmentHeader());
    067        this.segment = segment;
    068        defaultAttributeNames.add("AnnotationDefault");
    069        defaultAttributeNames.add("RuntimeVisibleAnnotations");
    070        defaultAttributeNames.add("RuntimeInvisibleAnnotations");
    071        defaultAttributeNames.add("RuntimeVisibleParameterAnnotations");
    072        defaultAttributeNames.add("RuntimeInvisibleParameterAnnotations");
    073        defaultAttributeNames.add("Code");
    074        defaultAttributeNames.add("LineNumberTable");
    075        defaultAttributeNames.add("LocalVariableTable");
    076        defaultAttributeNames.add("LocalVariableTypeTable");
    077        defaultAttributeNames.add("ConstantValue");
    078        defaultAttributeNames.add("Deprecated");
    079        defaultAttributeNames.add("EnclosingMethod");
    080        defaultAttributeNames.add("Exceptions");
    081        defaultAttributeNames.add("InnerClasses");
    082        defaultAttributeNames.add("Signature");
    083        defaultAttributeNames.add("SourceFile");
    084    }
    085
    086    private void addCharacters(final List<Character> chars, final char[] charArray) {
    087        for (final char element : charArray) {
    088            chars.add(Character.valueOf(element));
    089        }
    090    }
    091
    092    public void addCPClass(final String className) {
    093        getCPClass(className);
    094    }
    095
    096    void addCPUtf8(final String utf8) {
    097        getCPUtf8(utf8);
    098    }
    099
    100    private void addIndices() {
    101        for (final Set<? extends ConstantPoolEntry> set : Arrays.asList(cp_Utf8, cp_Int, cp_Float, cp_Long, cp_Double, cp_String, cp_Class, cp_Signature,
    102                cp_Descr, cp_Field, cp_Method, cp_Imethod)) {
    103            int j = 0;
    104            for (final ConstantPoolEntry entry : set) {
    105                entry.setIndex(j);
    106                j++;
    107            }
    108        }
    109        final Map<CPClass, Integer> classNameToIndex = new HashMap<>();
    110        cp_Field.forEach(mOrF -> {
    111            final CPClass cpClassName = mOrF.getClassName();
    112            final Integer index = classNameToIndex.get(cpClassName);
    113            if (index == null) {
    114                classNameToIndex.put(cpClassName, Integer.valueOf(1));
    115                mOrF.setIndexInClass(0);
    116            } else {
    117                final int theIndex = index.intValue();
    118                mOrF.setIndexInClass(theIndex);
    119                classNameToIndex.put(cpClassName, Integer.valueOf(theIndex + 1));
    120            }
    121        });
    122        classNameToIndex.clear();
    123        final Map<CPClass, Integer> classNameToConstructorIndex = new HashMap<>();
    124        cp_Method.forEach(mOrF -> {
    125            final CPClass cpClassName = mOrF.getClassName();
    126            final Integer index = classNameToIndex.get(cpClassName);
    127            if (index == null) {
    128                classNameToIndex.put(cpClassName, Integer.valueOf(1));
    129                mOrF.setIndexInClass(0);
    130            } else {
    131                final int theIndex = index.intValue();
    132                mOrF.setIndexInClass(theIndex);
    133                classNameToIndex.put(cpClassName, Integer.valueOf(theIndex + 1));
    134            }
    135            if (mOrF.getDesc().getName().equals("<init>")) {
    136                final Integer constructorIndex = classNameToConstructorIndex.get(cpClassName);
    137                if (constructorIndex == null) {
    138                    classNameToConstructorIndex.put(cpClassName, Integer.valueOf(1));
    139                    mOrF.setIndexInClassForConstructor(0);
    140                } else {
    141                    final int theIndex = constructorIndex.intValue();
    142                    mOrF.setIndexInClassForConstructor(theIndex);
    143                    classNameToConstructorIndex.put(cpClassName, Integer.valueOf(theIndex + 1));
    144                }
    145            }
    146        });
    147    }
    148
    149    public boolean existsCpClass(final String className) {
    150        return stringsToCpClass.containsKey(className);
    151    }
    152
    153    /**
    154     * 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
    155     * while classes were being read.
    156     */
    157    public void finaliseBands() {
    158        addCPUtf8("");
    159        removeSignaturesFromCpUTF8();
    160        addIndices();
    161        segmentHeader.setCp_Utf8_count(cp_Utf8.size());
    162        segmentHeader.setCp_Int_count(cp_Int.size());
    163        segmentHeader.setCp_Float_count(cp_Float.size());
    164        segmentHeader.setCp_Long_count(cp_Long.size());
    165        segmentHeader.setCp_Double_count(cp_Double.size());
    166        segmentHeader.setCp_String_count(cp_String.size());
    167        segmentHeader.setCp_Class_count(cp_Class.size());
    168        segmentHeader.setCp_Signature_count(cp_Signature.size());
    169        segmentHeader.setCp_Descr_count(cp_Descr.size());
    170        segmentHeader.setCp_Field_count(cp_Field.size());
    171        segmentHeader.setCp_Method_count(cp_Method.size());
    172        segmentHeader.setCp_Imethod_count(cp_Imethod.size());
    173    }
    174
    175    public CPConstant<?> getConstant(final Object value) {
    176        CPConstant<?> constant = objectsToCPConstant.get(value);
    177        if (constant == null) {
    178            if (value instanceof Integer) {
    179                constant = new CPInt(((Integer) value).intValue());
    180                cp_Int.add((CPInt) constant);
    181            } else if (value instanceof Long) {
    182                constant = new CPLong(((Long) value).longValue());
    183                cp_Long.add((CPLong) constant);
    184            } else if (value instanceof Float) {
    185                constant = new CPFloat(((Float) value).floatValue());
    186                cp_Float.add((CPFloat) constant);
    187            } else if (value instanceof Double) {
    188                constant = new CPDouble(((Double) value).doubleValue());
    189                cp_Double.add((CPDouble) constant);
    190            } else if (value instanceof String) {
    191                constant = new CPString(getCPUtf8((String) value));
    192                cp_String.add((CPString) constant);
    193            } else if (value instanceof Type) {
    194                String className = ((Type) value).getClassName();
    195                if (className.endsWith("[]")) {
    196                    className = "[L" + className.substring(0, className.length() - 2);
    197                    while (className.endsWith("[]")) {
    198                        className = "[" + className.substring(0, className.length() - 2);
    199                    }
    200                    className += ";";
    201                }
    202                constant = getCPClass(className);
    203            }
    204            objectsToCPConstant.put(value, constant);
    205        }
    206        return constant;
    207    }
    208
    209    public CPClass getCPClass(String className) {
    210        if (className == null) {
    211            return null;
    212        }
    213        className = className.replace('.', '/');
    214        CPClass cpClass = stringsToCpClass.get(className);
    215        if (cpClass == null) {
    216            final CPUTF8 cpUtf8 = getCPUtf8(className);
    217            cpClass = new CPClass(cpUtf8);
    218            cp_Class.add(cpClass);
    219            stringsToCpClass.put(className, cpClass);
    220        }
    221        if (cpClass.isInnerClass()) {
    222            segment.getClassBands().currentClassReferencesInnerClass(cpClass);
    223        }
    224        return cpClass;
    225    }
    226
    227    public CPMethodOrField getCPField(final CPClass cpClass, final String name, final String desc) {
    228        final String key = cpClass.toString() + ":" + name + ":" + desc;
    229        CPMethodOrField cpF = stringsToCpField.get(key);
    230        if (cpF == null) {
    231            final CPNameAndType nAndT = getCPNameAndType(name, desc);
    232            cpF = new CPMethodOrField(cpClass, nAndT);
    233            cp_Field.add(cpF);
    234            stringsToCpField.put(key, cpF);
    235        }
    236        return cpF;
    237    }
    238
    239    public CPMethodOrField getCPField(final String owner, final String name, final String desc) {
    240        return getCPField(getCPClass(owner), name, desc);
    241    }
    242
    243    public CPMethodOrField getCPIMethod(final CPClass cpClass, final String name, final String desc) {
    244        final String key = cpClass.toString() + ":" + name + ":" + desc;
    245        CPMethodOrField cpIM = stringsToCpIMethod.get(key);
    246        if (cpIM == null) {
    247            final CPNameAndType nAndT = getCPNameAndType(name, desc);
    248            cpIM = new CPMethodOrField(cpClass, nAndT);
    249            cp_Imethod.add(cpIM);
    250            stringsToCpIMethod.put(key, cpIM);
    251        }
    252        return cpIM;
    253    }
    254
    255    public CPMethodOrField getCPIMethod(final String owner, final String name, final String desc) {
    256        return getCPIMethod(getCPClass(owner), name, desc);
    257    }
    258
    259    public CPMethodOrField getCPMethod(final CPClass cpClass, final String name, final String desc) {
    260        final String key = cpClass.toString() + ":" + name + ":" + desc;
    261        CPMethodOrField cpM = stringsToCpMethod.get(key);
    262        if (cpM == null) {
    263            final CPNameAndType nAndT = getCPNameAndType(name, desc);
    264            cpM = new CPMethodOrField(cpClass, nAndT);
    265            cp_Method.add(cpM);
    266            stringsToCpMethod.put(key, cpM);
    267        }
    268        return cpM;
    269    }
    270
    271    public CPMethodOrField getCPMethod(final String owner, final String name, final String desc) {
    272        return getCPMethod(getCPClass(owner), name, desc);
    273    }
    274
    275    public CPNameAndType getCPNameAndType(final String name, final String signature) {
    276        final String descr = name + ":" + signature;
    277        CPNameAndType nameAndType = stringsToCpNameAndType.get(descr);
    278        if (nameAndType == null) {
    279            nameAndType = new CPNameAndType(getCPUtf8(name), getCPSignature(signature));
    280            stringsToCpNameAndType.put(descr, nameAndType);
    281            cp_Descr.add(nameAndType);
    282        }
    283        return nameAndType;
    284    }
    285
    286    public CPSignature getCPSignature(final String signature) {
    287        if (signature == null) {
    288            return null;
    289        }
    290        CPSignature cpS = stringsToCpSignature.get(signature);
    291        if (cpS == null) {
    292            final List<CPClass> cpClasses = new ArrayList<>();
    293            CPUTF8 signatureUTF8;
    294            if (signature.length() > 1 && signature.indexOf('L') != -1) {
    295                final List<String> classes = new ArrayList<>();
    296                final char[] chars = signature.toCharArray();
    297                final StringBuilder signatureString = new StringBuilder();
    298                for (int i = 0; i < chars.length; i++) {
    299                    signatureString.append(chars[i]);
    300                    if (chars[i] == 'L') {
    301                        final StringBuilder className = new StringBuilder();
    302                        for (int j = i + 1; j < chars.length; j++) {
    303                            final char c = chars[j];
    304                            if (!Character.isLetter(c) && !Character.isDigit(c) && c != '/' && c != '$' && c != '_') {
    305                                classes.add(className.toString());
    306                                i = j - 1;
    307                                break;
    308                            }
    309                            className.append(c);
    310                        }
    311                    }
    312                }
    313                removeCpUtf8(signature);
    314                for (String className : classes) {
    315                    CPClass cpClass = null;
    316                    if (className != null) {
    317                        className = className.replace('.', '/');
    318                        cpClass = stringsToCpClass.get(className);
    319                        if (cpClass == null) {
    320                            final CPUTF8 cpUtf8 = getCPUtf8(className);
    321                            cpClass = new CPClass(cpUtf8);
    322                            cp_Class.add(cpClass);
    323                            stringsToCpClass.put(className, cpClass);
    324                        }
    325                    }
    326                    cpClasses.add(cpClass);
    327                }
    328
    329                signatureUTF8 = getCPUtf8(signatureString.toString());
    330            } else {
    331                signatureUTF8 = getCPUtf8(signature);
    332            }
    333            cpS = new CPSignature(signature, signatureUTF8, cpClasses);
    334            cp_Signature.add(cpS);
    335            stringsToCpSignature.put(signature, cpS);
    336        }
    337        return cpS;
    338    }
    339
    340    public CPUTF8 getCPUtf8(final String utf8) {
    341        if (utf8 == null) {
    342            return null;
    343        }
    344        CPUTF8 cpUtf8 = stringsToCpUtf8.get(utf8);
    345        if (cpUtf8 == null) {
    346            cpUtf8 = new CPUTF8(utf8);
    347            cp_Utf8.add(cpUtf8);
    348            stringsToCpUtf8.put(utf8, cpUtf8);
    349        }
    350        return cpUtf8;
    351    }
    352
    353    @Override
    354    public void pack(final OutputStream out) throws IOException, Pack200Exception {
    355        PackingUtils.log("Writing constant pool bands...");
    356        writeCpUtf8(out);
    357        writeCpInt(out);
    358        writeCpFloat(out);
    359        writeCpLong(out);
    360        writeCpDouble(out);
    361        writeCpString(out);
    362        writeCpClass(out);
    363        writeCpSignature(out);
    364        writeCpDescr(out);
    365        writeCpMethodOrField(cp_Field, out, "cp_Field");
    366        writeCpMethodOrField(cp_Method, out, "cp_Method");
    367        writeCpMethodOrField(cp_Imethod, out, "cp_Imethod");
    368    }
    369
    370    private void removeCpUtf8(final String string) {
    371        final CPUTF8 utf8 = stringsToCpUtf8.get(string);
    372        if (utf8 != null && stringsToCpClass.get(string) == null) { // don't remove if strings are also in cpclass
    373            stringsToCpUtf8.remove(string);
    374            cp_Utf8.remove(utf8);
    375        }
    376    }
    377
    378    private void removeSignaturesFromCpUTF8() {
    379        cp_Signature.forEach(signature -> {
    380            final String sigStr = signature.getUnderlyingString();
    381            final CPUTF8 utf8 = signature.getSignatureForm();
    382            final String form = utf8.getUnderlyingString();
    383            if (!sigStr.equals(form)) {
    384                removeCpUtf8(sigStr);
    385            }
    386        });
    387    }
    388
    389    private void writeCpClass(final OutputStream out) throws IOException, Pack200Exception {
    390        PackingUtils.log("Writing " + cp_Class.size() + " Class entries...");
    391        final int[] cpClass = new int[cp_Class.size()];
    392        int i = 0;
    393        for (final CPClass cpCl : cp_Class) {
    394            cpClass[i] = cpCl.getIndexInCpUtf8();
    395            i++;
    396        }
    397        final byte[] encodedBand = encodeBandInt("cpClass", cpClass, Codec.UDELTA5);
    398        out.write(encodedBand);
    399        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpClass[" + cpClass.length + "]");
    400    }
    401
    402    private void writeCpDescr(final OutputStream out) throws IOException, Pack200Exception {
    403        PackingUtils.log("Writing " + cp_Descr.size() + " Descriptor entries...");
    404        final int[] cpDescrName = new int[cp_Descr.size()];
    405        final int[] cpDescrType = new int[cp_Descr.size()];
    406        int i = 0;
    407        for (final CPNameAndType nameAndType : cp_Descr) {
    408            cpDescrName[i] = nameAndType.getNameIndex();
    409            cpDescrType[i] = nameAndType.getTypeIndex();
    410            i++;
    411        }
    412
    413        byte[] encodedBand = encodeBandInt("cp_Descr_Name", cpDescrName, Codec.DELTA5);
    414        out.write(encodedBand);
    415        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Descr_Name[" + cpDescrName.length + "]");
    416
    417        encodedBand = encodeBandInt("cp_Descr_Type", cpDescrType, Codec.UDELTA5);
    418        out.write(encodedBand);
    419        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Descr_Type[" + cpDescrType.length + "]");
    420    }
    421
    422    private void writeCpDouble(final OutputStream out) throws IOException, Pack200Exception {
    423        PackingUtils.log("Writing " + cp_Double.size() + " Double entries...");
    424        final int[] highBits = new int[cp_Double.size()];
    425        final int[] loBits = new int[cp_Double.size()];
    426        int i = 0;
    427        for (final CPDouble dbl : cp_Double) {
    428            final long l = Double.doubleToLongBits(dbl.getDouble());
    429            highBits[i] = (int) (l >> 32);
    430            loBits[i] = (int) l;
    431            i++;
    432        }
    433        byte[] encodedBand = encodeBandInt("cp_Double_hi", highBits, Codec.UDELTA5);
    434        out.write(encodedBand);
    435        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Double_hi[" + highBits.length + "]");
    436
    437        encodedBand = encodeBandInt("cp_Double_lo", loBits, Codec.DELTA5);
    438        out.write(encodedBand);
    439        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Double_lo[" + loBits.length + "]");
    440    }
    441
    442    private void writeCpFloat(final OutputStream out) throws IOException, Pack200Exception {
    443        PackingUtils.log("Writing " + cp_Float.size() + " Float entries...");
    444        final int[] cpFloat = new int[cp_Float.size()];
    445        int i = 0;
    446        for (final CPFloat fl : cp_Float) {
    447            cpFloat[i] = Float.floatToIntBits(fl.getFloat());
    448            i++;
    449        }
    450        final byte[] encodedBand = encodeBandInt("cp_Float", cpFloat, Codec.UDELTA5);
    451        out.write(encodedBand);
    452        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Float[" + cpFloat.length + "]");
    453    }
    454
    455    private void writeCpInt(final OutputStream out) throws IOException, Pack200Exception {
    456        PackingUtils.log("Writing " + cp_Int.size() + " Integer entries...");
    457        final int[] cpInt = new int[cp_Int.size()];
    458        int i = 0;
    459        for (final CPInt integer : cp_Int) {
    460            cpInt[i] = integer.getInt();
    461            i++;
    462        }
    463        final byte[] encodedBand = encodeBandInt("cp_Int", cpInt, Codec.UDELTA5);
    464        out.write(encodedBand);
    465        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Int[" + cpInt.length + "]");
    466    }
    467
    468    private void writeCpLong(final OutputStream out) throws IOException, Pack200Exception {
    469        PackingUtils.log("Writing " + cp_Long.size() + " Long entries...");
    470        final int[] highBits = new int[cp_Long.size()];
    471        final int[] loBits = new int[cp_Long.size()];
    472        int i = 0;
    473        for (final CPLong lng : cp_Long) {
    474            final long l = lng.getLong();
    475            highBits[i] = (int) (l >> 32);
    476            loBits[i] = (int) l;
    477            i++;
    478        }
    479        byte[] encodedBand = encodeBandInt("cp_Long_hi", highBits, Codec.UDELTA5);
    480        out.write(encodedBand);
    481        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Long_hi[" + highBits.length + "]");
    482
    483        encodedBand = encodeBandInt("cp_Long_lo", loBits, Codec.DELTA5);
    484        out.write(encodedBand);
    485        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Long_lo[" + loBits.length + "]");
    486    }
    487
    488    private void writeCpMethodOrField(final Set<CPMethodOrField> cp, final OutputStream out, final String name) throws IOException, Pack200Exception {
    489        PackingUtils.log("Writing " + cp.size() + " Method and Field entries...");
    490        final int[] cp_methodOrField_class = new int[cp.size()];
    491        final int[] cp_methodOrField_desc = new int[cp.size()];
    492        int i = 0;
    493        for (final CPMethodOrField mOrF : cp) {
    494            cp_methodOrField_class[i] = mOrF.getClassIndex();
    495            cp_methodOrField_desc[i] = mOrF.getDescIndex();
    496            i++;
    497        }
    498        byte[] encodedBand = encodeBandInt(name + "_class", cp_methodOrField_class, Codec.DELTA5);
    499        out.write(encodedBand);
    500        PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + name + "_class[" + cp_methodOrField_class.length + "]");
    501
    502        encodedBand = encodeBandInt(name + "_desc", cp_methodOrField_desc, Codec.UDELTA5);
    503        out.write(encodedBand);
    504        PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + name + "_desc[" + cp_methodOrField_desc.length + "]");
    505    }
    506
    507    private void writeCpSignature(final OutputStream out) throws IOException, Pack200Exception {
    508        PackingUtils.log("Writing " + cp_Signature.size() + " Signature entries...");
    509        final int[] cpSignatureForm = new int[cp_Signature.size()];
    510        final List<CPClass> classes = new ArrayList<>();
    511        int i = 0;
    512        for (final CPSignature cpS : cp_Signature) {
    513            classes.addAll(cpS.getClasses());
    514            cpSignatureForm[i] = cpS.getIndexInCpUtf8();
    515            i++;
    516        }
    517        final int[] cpSignatureClasses = new int[classes.size()];
    518        Arrays.setAll(cpSignatureClasses, j -> classes.get(j).getIndex());
    519
    520        byte[] encodedBand = encodeBandInt("cpSignatureForm", cpSignatureForm, Codec.DELTA5);
    521        out.write(encodedBand);
    522        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpSignatureForm[" + cpSignatureForm.length + "]");
    523
    524        encodedBand = encodeBandInt("cpSignatureClasses", cpSignatureClasses, Codec.UDELTA5);
    525        out.write(encodedBand);
    526        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpSignatureClasses[" + cpSignatureClasses.length + "]");
    527    }
    528
    529    private void writeCpString(final OutputStream out) throws IOException, Pack200Exception {
    530        PackingUtils.log("Writing " + cp_String.size() + " String entries...");
    531        final int[] cpString = new int[cp_String.size()];
    532        int i = 0;
    533        for (final CPString cpStr : cp_String) {
    534            cpString[i] = cpStr.getIndexInCpUtf8();
    535            i++;
    536        }
    537        final byte[] encodedBand = encodeBandInt("cpString", cpString, Codec.UDELTA5);
    538        out.write(encodedBand);
    539        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpString[" + cpString.length + "]");
    540    }
    541
    542    private void writeCpUtf8(final OutputStream out) throws IOException, Pack200Exception {
    543        PackingUtils.log("Writing " + cp_Utf8.size() + " UTF8 entries...");
    544        final int[] cpUtf8Prefix = new int[cp_Utf8.size() - 2];
    545        final int[] cpUtf8Suffix = new int[cp_Utf8.size() - 1];
    546        final List<Character> chars = new ArrayList<>();
    547        final List<Integer> bigSuffix = new ArrayList<>();
    548        final List<Character> bigChars = new ArrayList<>();
    549        final Object[] cpUtf8Array = cp_Utf8.toArray();
    550        final String first = ((CPUTF8) cpUtf8Array[1]).getUnderlyingString();
    551        cpUtf8Suffix[0] = first.length();
    552        addCharacters(chars, first.toCharArray());
    553        for (int i = 2; i < cpUtf8Array.length; i++) {
    554            final char[] previous = ((CPUTF8) cpUtf8Array[i - 1]).getUnderlyingString().toCharArray();
    555            String currentStr = ((CPUTF8) cpUtf8Array[i]).getUnderlyingString();
    556            final char[] current = currentStr.toCharArray();
    557            int prefix = 0;
    558            for (int j = 0; j < previous.length; j++) {
    559                if (previous[j] != current[j]) {
    560                    break;
    561                }
    562                prefix++;
    563            }
    564            cpUtf8Prefix[i - 2] = prefix;
    565            currentStr = currentStr.substring(prefix);
    566            final char[] suffix = currentStr.toCharArray();
    567            if (suffix.length > 1000) { // big suffix (1000 is arbitrary - can we
    568                // do better?)
    569                cpUtf8Suffix[i - 1] = 0;
    570                bigSuffix.add(Integer.valueOf(suffix.length));
    571                addCharacters(bigChars, suffix);
    572            } else {
    573                cpUtf8Suffix[i - 1] = suffix.length;
    574                addCharacters(chars, suffix);
    575            }
    576        }
    577        final int[] cpUtf8Chars = new int[chars.size()];
    578        final int[] cpUtf8BigSuffix = new int[bigSuffix.size()];
    579        final int[][] cpUtf8BigChars = new int[bigSuffix.size()][];
    580        Arrays.setAll(cpUtf8Chars, i -> chars.get(i).charValue());
    581        for (int i = 0; i < cpUtf8BigSuffix.length; i++) {
    582            final int numBigChars = bigSuffix.get(i).intValue();
    583            cpUtf8BigSuffix[i] = numBigChars;
    584            cpUtf8BigChars[i] = new int[numBigChars];
    585            Arrays.setAll(cpUtf8BigChars[i], j -> bigChars.remove(0).charValue());
    586        }
    587
    588        byte[] encodedBand = encodeBandInt("cpUtf8Prefix", cpUtf8Prefix, Codec.DELTA5);
    589        out.write(encodedBand);
    590        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpUtf8Prefix[" + cpUtf8Prefix.length + "]");
    591
    592        encodedBand = encodeBandInt("cpUtf8Suffix", cpUtf8Suffix, Codec.UNSIGNED5);
    593        out.write(encodedBand);
    594        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpUtf8Suffix[" + cpUtf8Suffix.length + "]");
    595
    596        encodedBand = encodeBandInt("cpUtf8Chars", cpUtf8Chars, Codec.CHAR3);
    597        out.write(encodedBand);
    598        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpUtf8Chars[" + cpUtf8Chars.length + "]");
    599
    600        encodedBand = encodeBandInt("cpUtf8BigSuffix", cpUtf8BigSuffix, Codec.DELTA5);
    601        out.write(encodedBand);
    602        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpUtf8BigSuffix[" + cpUtf8BigSuffix.length + "]");
    603
    604        for (int i = 0; i < cpUtf8BigChars.length; i++) {
    605            encodedBand = encodeBandInt("cpUtf8BigChars " + i, cpUtf8BigChars[i], Codec.DELTA5);
    606            out.write(encodedBand);
    607            PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpUtf8BigChars" + i + "[" + cpUtf8BigChars[i].length + "]");
    608        }
    609    }
    610
    611}