    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,
    102                                cp_String, cp_Class, cp_Signature, 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
    155     * calculate/complete anything it could not do 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 != '$')
    305                                && (c != '_')) {
    306                                classes.add(className.toString());
    307                                i = j - 1;
    308                                break;
    309                            }
    310                            className.append(c);
    311                        }
    312                    }
    313                }
    314                removeCpUtf8(signature);
    315                for (String className : classes) {
    316                    CPClass cpClass = null;
    317                    if (className != null) {
    318                        className = className.replace('.', '/');
    319                        cpClass = stringsToCpClass.get(className);
    320                        if (cpClass == null) {
    321                            final CPUTF8 cpUtf8 = getCPUtf8(className);
    322                            cpClass = new CPClass(cpUtf8);
    323                            cp_Class.add(cpClass);
    324                            stringsToCpClass.put(className, cpClass);
    325                        }
    326                    }
    327                    cpClasses.add(cpClass);
    328                }
    329
    330                signatureUTF8 = getCPUtf8(signatureString.toString());
    331            } else {
    332                signatureUTF8 = getCPUtf8(signature);
    333            }
    334            cpS = new CPSignature(signature, signatureUTF8, cpClasses);
    335            cp_Signature.add(cpS);
    336            stringsToCpSignature.put(signature, cpS);
    337        }
    338        return cpS;
    339    }
    340
    341    public CPUTF8 getCPUtf8(final String utf8) {
    342        if (utf8 == null) {
    343            return null;
    344        }
    345        CPUTF8 cpUtf8 = stringsToCpUtf8.get(utf8);
    346        if (cpUtf8 == null) {
    347            cpUtf8 = new CPUTF8(utf8);
    348            cp_Utf8.add(cpUtf8);
    349            stringsToCpUtf8.put(utf8, cpUtf8);
    350        }
    351        return cpUtf8;
    352    }
    353
    354    @Override
    355    public void pack(final OutputStream out) throws IOException, Pack200Exception {
    356        PackingUtils.log("Writing constant pool bands...");
    357        writeCpUtf8(out);
    358        writeCpInt(out);
    359        writeCpFloat(out);
    360        writeCpLong(out);
    361        writeCpDouble(out);
    362        writeCpString(out);
    363        writeCpClass(out);
    364        writeCpSignature(out);
    365        writeCpDescr(out);
    366        writeCpMethodOrField(cp_Field, out, "cp_Field");
    367        writeCpMethodOrField(cp_Method, out, "cp_Method");
    368        writeCpMethodOrField(cp_Imethod, out, "cp_Imethod");
    369    }
    370
    371    private void removeCpUtf8(final String string) {
    372        final CPUTF8 utf8 = stringsToCpUtf8.get(string);
    373        if ((utf8 != null) && (stringsToCpClass.get(string) == null)) { // don't remove if strings are also in cpclass
    374            stringsToCpUtf8.remove(string);
    375            cp_Utf8.remove(utf8);
    376        }
    377    }
    378
    379    private void removeSignaturesFromCpUTF8() {
    380        cp_Signature.forEach(signature -> {
    381            final String sigStr = signature.getUnderlyingString();
    382            final CPUTF8 utf8 = signature.getSignatureForm();
    383            final String form = utf8.getUnderlyingString();
    384            if (!sigStr.equals(form)) {
    385                removeCpUtf8(sigStr);
    386            }
    387        });
    388    }
    389
    390    private void writeCpClass(final OutputStream out) throws IOException, Pack200Exception {
    391        PackingUtils.log("Writing " + cp_Class.size() + " Class entries...");
    392        final int[] cpClass = new int[cp_Class.size()];
    393        int i = 0;
    394        for (final CPClass cpCl : cp_Class) {
    395            cpClass[i] = cpCl.getIndexInCpUtf8();
    396            i++;
    397        }
    398        final byte[] encodedBand = encodeBandInt("cpClass", cpClass, Codec.UDELTA5);
    399        out.write(encodedBand);
    400        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpClass[" + cpClass.length + "]");
    401    }
    402
    403    private void writeCpDescr(final OutputStream out) throws IOException, Pack200Exception {
    404        PackingUtils.log("Writing " + cp_Descr.size() + " Descriptor entries...");
    405        final int[] cpDescrName = new int[cp_Descr.size()];
    406        final int[] cpDescrType = new int[cp_Descr.size()];
    407        int i = 0;
    408        for (final CPNameAndType nameAndType : cp_Descr) {
    409            cpDescrName[i] = nameAndType.getNameIndex();
    410            cpDescrType[i] = nameAndType.getTypeIndex();
    411            i++;
    412        }
    413
    414        byte[] encodedBand = encodeBandInt("cp_Descr_Name", cpDescrName, Codec.DELTA5);
    415        out.write(encodedBand);
    416        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Descr_Name[" + cpDescrName.length + "]");
    417
    418        encodedBand = encodeBandInt("cp_Descr_Type", cpDescrType, Codec.UDELTA5);
    419        out.write(encodedBand);
    420        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Descr_Type[" + cpDescrType.length + "]");
    421    }
    422
    423    private void writeCpDouble(final OutputStream out) throws IOException, Pack200Exception {
    424        PackingUtils.log("Writing " + cp_Double.size() + " Double entries...");
    425        final int[] highBits = new int[cp_Double.size()];
    426        final int[] loBits = new int[cp_Double.size()];
    427        int i = 0;
    428        for (final CPDouble dbl : cp_Double) {
    429            final long l = Double.doubleToLongBits(dbl.getDouble());
    430            highBits[i] = (int) (l >> 32);
    431            loBits[i] = (int) l;
    432            i++;
    433        }
    434        byte[] encodedBand = encodeBandInt("cp_Double_hi", highBits, Codec.UDELTA5);
    435        out.write(encodedBand);
    436        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Double_hi[" + highBits.length + "]");
    437
    438        encodedBand = encodeBandInt("cp_Double_lo", loBits, Codec.DELTA5);
    439        out.write(encodedBand);
    440        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Double_lo[" + loBits.length + "]");
    441    }
    442
    443    private void writeCpFloat(final OutputStream out) throws IOException, Pack200Exception {
    444        PackingUtils.log("Writing " + cp_Float.size() + " Float entries...");
    445        final int[] cpFloat = new int[cp_Float.size()];
    446        int i = 0;
    447        for (final CPFloat fl : cp_Float) {
    448            cpFloat[i] = Float.floatToIntBits(fl.getFloat());
    449            i++;
    450        }
    451        final byte[] encodedBand = encodeBandInt("cp_Float", cpFloat, Codec.UDELTA5);
    452        out.write(encodedBand);
    453        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Float[" + cpFloat.length + "]");
    454    }
    455
    456    private void writeCpInt(final OutputStream out) throws IOException, Pack200Exception {
    457        PackingUtils.log("Writing " + cp_Int.size() + " Integer entries...");
    458        final int[] cpInt = new int[cp_Int.size()];
    459        int i = 0;
    460        for (final CPInt integer : cp_Int) {
    461            cpInt[i] = integer.getInt();
    462            i++;
    463        }
    464        final byte[] encodedBand = encodeBandInt("cp_Int", cpInt, Codec.UDELTA5);
    465        out.write(encodedBand);
    466        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Int[" + cpInt.length + "]");
    467    }
    468
    469    private void writeCpLong(final OutputStream out) throws IOException, Pack200Exception {
    470        PackingUtils.log("Writing " + cp_Long.size() + " Long entries...");
    471        final int[] highBits = new int[cp_Long.size()];
    472        final int[] loBits = new int[cp_Long.size()];
    473        int i = 0;
    474        for (final CPLong lng : cp_Long) {
    475            final long l = lng.getLong();
    476            highBits[i] = (int) (l >> 32);
    477            loBits[i] = (int) l;
    478            i++;
    479        }
    480        byte[] encodedBand = encodeBandInt("cp_Long_hi", highBits, Codec.UDELTA5);
    481        out.write(encodedBand);
    482        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Long_hi[" + highBits.length + "]");
    483
    484        encodedBand = encodeBandInt("cp_Long_lo", loBits, Codec.DELTA5);
    485        out.write(encodedBand);
    486        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Long_lo[" + loBits.length + "]");
    487    }
    488
    489    private void writeCpMethodOrField(final Set<CPMethodOrField> cp, final OutputStream out, final String name)
    490        throws IOException, Pack200Exception {
    491        PackingUtils.log("Writing " + cp.size() + " Method and Field entries...");
    492        final int[] cp_methodOrField_class = new int[cp.size()];
    493        final int[] cp_methodOrField_desc = new int[cp.size()];
    494        int i = 0;
    495        for (final CPMethodOrField mOrF : cp) {
    496            cp_methodOrField_class[i] = mOrF.getClassIndex();
    497            cp_methodOrField_desc[i] = mOrF.getDescIndex();
    498            i++;
    499        }
    500        byte[] encodedBand = encodeBandInt(name + "_class", cp_methodOrField_class, Codec.DELTA5);
    501        out.write(encodedBand);
    502        PackingUtils.log(
    503            "Wrote " + encodedBand.length + " bytes from " + name + "_class[" + cp_methodOrField_class.length + "]");
    504
    505        encodedBand = encodeBandInt(name + "_desc", cp_methodOrField_desc, Codec.UDELTA5);
    506        out.write(encodedBand);
    507        PackingUtils
    508            .log("Wrote " + encodedBand.length + " bytes from " + name + "_desc[" + cp_methodOrField_desc.length + "]");
    509    }
    510
    511    private void writeCpSignature(final OutputStream out) throws IOException, Pack200Exception {
    512        PackingUtils.log("Writing " + cp_Signature.size() + " Signature entries...");
    513        final int[] cpSignatureForm = new int[cp_Signature.size()];
    514        final List<CPClass> classes = new ArrayList<>();
    515        int i = 0;
    516        for (final CPSignature cpS : cp_Signature) {
    517            classes.addAll(cpS.getClasses());
    518            cpSignatureForm[i] = cpS.getIndexInCpUtf8();
    519            i++;
    520        }
    521        final int[] cpSignatureClasses = new int[classes.size()];
    522        Arrays.setAll(cpSignatureClasses, j -> classes.get(j).getIndex());
    523
    524        byte[] encodedBand = encodeBandInt("cpSignatureForm", cpSignatureForm, Codec.DELTA5);
    525        out.write(encodedBand);
    526        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpSignatureForm[" + cpSignatureForm.length + "]");
    527
    528        encodedBand = encodeBandInt("cpSignatureClasses", cpSignatureClasses, Codec.UDELTA5);
    529        out.write(encodedBand);
    530        PackingUtils
    531            .log("Wrote " + encodedBand.length + " bytes from cpSignatureClasses[" + cpSignatureClasses.length + "]");
    532    }
    533
    534    private void writeCpString(final OutputStream out) throws IOException, Pack200Exception {
    535        PackingUtils.log("Writing " + cp_String.size() + " String entries...");
    536        final int[] cpString = new int[cp_String.size()];
    537        int i = 0;
    538        for (final CPString cpStr : cp_String) {
    539            cpString[i] = cpStr.getIndexInCpUtf8();
    540            i++;
    541        }
    542        final byte[] encodedBand = encodeBandInt("cpString", cpString, Codec.UDELTA5);
    543        out.write(encodedBand);
    544        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpString[" + cpString.length + "]");
    545    }
    546
    547    private void writeCpUtf8(final OutputStream out) throws IOException, Pack200Exception {
    548        PackingUtils.log("Writing " + cp_Utf8.size() + " UTF8 entries...");
    549        final int[] cpUtf8Prefix = new int[cp_Utf8.size() - 2];
    550        final int[] cpUtf8Suffix = new int[cp_Utf8.size() - 1];
    551        final List<Character> chars = new ArrayList<>();
    552        final List<Integer> bigSuffix = new ArrayList<>();
    553        final List<Character> bigChars = new ArrayList<>();
    554        final Object[] cpUtf8Array = cp_Utf8.toArray();
    555        final String first = ((CPUTF8) cpUtf8Array[1]).getUnderlyingString();
    556        cpUtf8Suffix[0] = first.length();
    557        addCharacters(chars, first.toCharArray());
    558        for (int i = 2; i < cpUtf8Array.length; i++) {
    559            final char[] previous = ((CPUTF8) cpUtf8Array[i - 1]).getUnderlyingString().toCharArray();
    560            String currentStr = ((CPUTF8) cpUtf8Array[i]).getUnderlyingString();
    561            final char[] current = currentStr.toCharArray();
    562            int prefix = 0;
    563            for (int j = 0; j < previous.length; j++) {
    564                if (previous[j] != current[j]) {
    565                    break;
    566                }
    567                prefix++;
    568            }
    569            cpUtf8Prefix[i - 2] = prefix;
    570            currentStr = currentStr.substring(prefix);
    571            final char[] suffix = currentStr.toCharArray();
    572            if (suffix.length > 1000) { // big suffix (1000 is arbitrary - can we
    573                // do better?)
    574                cpUtf8Suffix[i - 1] = 0;
    575                bigSuffix.add(Integer.valueOf(suffix.length));
    576                addCharacters(bigChars, suffix);
    577            } else {
    578                cpUtf8Suffix[i - 1] = suffix.length;
    579                addCharacters(chars, suffix);
    580            }
    581        }
    582        final int[] cpUtf8Chars = new int[chars.size()];
    583        final int[] cpUtf8BigSuffix = new int[bigSuffix.size()];
    584        final int[][] cpUtf8BigChars = new int[bigSuffix.size()][];
    585        Arrays.setAll(cpUtf8Chars, i -> chars.get(i).charValue());
    586        for (int i = 0; i < cpUtf8BigSuffix.length; i++) {
    587            final int numBigChars = bigSuffix.get(i).intValue();
    588            cpUtf8BigSuffix[i] = numBigChars;
    589            cpUtf8BigChars[i] = new int[numBigChars];
    590            Arrays.setAll(cpUtf8BigChars[i], j -> bigChars.remove(0).charValue());
    591        }
    592
    593        byte[] encodedBand = encodeBandInt("cpUtf8Prefix", cpUtf8Prefix, Codec.DELTA5);
    594        out.write(encodedBand);
    595        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpUtf8Prefix[" + cpUtf8Prefix.length + "]");
    596
    597        encodedBand = encodeBandInt("cpUtf8Suffix", cpUtf8Suffix, Codec.UNSIGNED5);
    598        out.write(encodedBand);
    599        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpUtf8Suffix[" + cpUtf8Suffix.length + "]");
    600
    601        encodedBand = encodeBandInt("cpUtf8Chars", cpUtf8Chars, Codec.CHAR3);
    602        out.write(encodedBand);
    603        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpUtf8Chars[" + cpUtf8Chars.length + "]");
    604
    605        encodedBand = encodeBandInt("cpUtf8BigSuffix", cpUtf8BigSuffix, Codec.DELTA5);
    606        out.write(encodedBand);
    607        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpUtf8BigSuffix[" + cpUtf8BigSuffix.length + "]");
    608
    609        for (int i = 0; i < cpUtf8BigChars.length; i++) {
    610            encodedBand = encodeBandInt("cpUtf8BigChars " + i, cpUtf8BigChars[i], Codec.DELTA5);
    611            out.write(encodedBand);
    612            PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpUtf8BigChars" + i + "["
    613                + cpUtf8BigChars[i].length + "]");
    614        }
    615    }
    616
    617}