    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        final CPClass cpClass = stringsToCpClass.get(className);
    151        return cpClass != null;
    152    }
    153
    154    /**
    155     * All input classes for the segment have now been read in, so this method is called so that this class can
    156     * calculate/complete anything it could not do while classes were being read.
    157     */
    158    public void finaliseBands() {
    159        addCPUtf8("");
    160        removeSignaturesFromCpUTF8();
    161        addIndices();
    162        segmentHeader.setCp_Utf8_count(cp_Utf8.size());
    163        segmentHeader.setCp_Int_count(cp_Int.size());
    164        segmentHeader.setCp_Float_count(cp_Float.size());
    165        segmentHeader.setCp_Long_count(cp_Long.size());
    166        segmentHeader.setCp_Double_count(cp_Double.size());
    167        segmentHeader.setCp_String_count(cp_String.size());
    168        segmentHeader.setCp_Class_count(cp_Class.size());
    169        segmentHeader.setCp_Signature_count(cp_Signature.size());
    170        segmentHeader.setCp_Descr_count(cp_Descr.size());
    171        segmentHeader.setCp_Field_count(cp_Field.size());
    172        segmentHeader.setCp_Method_count(cp_Method.size());
    173        segmentHeader.setCp_Imethod_count(cp_Imethod.size());
    174    }
    175
    176    public CPConstant<?> getConstant(final Object value) {
    177        CPConstant<?> constant = objectsToCPConstant.get(value);
    178        if (constant == null) {
    179            if (value instanceof Integer) {
    180                constant = new CPInt(((Integer) value).intValue());
    181                cp_Int.add((CPInt) constant);
    182            } else if (value instanceof Long) {
    183                constant = new CPLong(((Long) value).longValue());
    184                cp_Long.add((CPLong) constant);
    185            } else if (value instanceof Float) {
    186                constant = new CPFloat(((Float) value).floatValue());
    187                cp_Float.add((CPFloat) constant);
    188            } else if (value instanceof Double) {
    189                constant = new CPDouble(((Double) value).doubleValue());
    190                cp_Double.add((CPDouble) constant);
    191            } else if (value instanceof String) {
    192                constant = new CPString(getCPUtf8((String) value));
    193                cp_String.add((CPString) constant);
    194            } else if (value instanceof Type) {
    195                String className = ((Type) value).getClassName();
    196                if (className.endsWith("[]")) {
    197                    className = "[L" + className.substring(0, className.length() - 2);
    198                    while (className.endsWith("[]")) {
    199                        className = "[" + className.substring(0, className.length() - 2);
    200                    }
    201                    className += ";";
    202                }
    203                constant = getCPClass(className);
    204            }
    205            objectsToCPConstant.put(value, constant);
    206        }
    207        return constant;
    208    }
    209
    210    public CPClass getCPClass(String className) {
    211        if (className == null) {
    212            return null;
    213        }
    214        className = className.replace('.', '/');
    215        CPClass cpClass = stringsToCpClass.get(className);
    216        if (cpClass == null) {
    217            final CPUTF8 cpUtf8 = getCPUtf8(className);
    218            cpClass = new CPClass(cpUtf8);
    219            cp_Class.add(cpClass);
    220            stringsToCpClass.put(className, cpClass);
    221        }
    222        if (cpClass.isInnerClass()) {
    223            segment.getClassBands().currentClassReferencesInnerClass(cpClass);
    224        }
    225        return cpClass;
    226    }
    227
    228    public CPMethodOrField getCPField(final CPClass cpClass, final String name, final String desc) {
    229        final String key = cpClass.toString() + ":" + name + ":" + desc;
    230        CPMethodOrField cpF = stringsToCpField.get(key);
    231        if (cpF == null) {
    232            final CPNameAndType nAndT = getCPNameAndType(name, desc);
    233            cpF = new CPMethodOrField(cpClass, nAndT);
    234            cp_Field.add(cpF);
    235            stringsToCpField.put(key, cpF);
    236        }
    237        return cpF;
    238    }
    239
    240    public CPMethodOrField getCPField(final String owner, final String name, final String desc) {
    241        return getCPField(getCPClass(owner), name, desc);
    242    }
    243
    244    public CPMethodOrField getCPIMethod(final CPClass cpClass, final String name, final String desc) {
    245        final String key = cpClass.toString() + ":" + name + ":" + desc;
    246        CPMethodOrField cpIM = stringsToCpIMethod.get(key);
    247        if (cpIM == null) {
    248            final CPNameAndType nAndT = getCPNameAndType(name, desc);
    249            cpIM = new CPMethodOrField(cpClass, nAndT);
    250            cp_Imethod.add(cpIM);
    251            stringsToCpIMethod.put(key, cpIM);
    252        }
    253        return cpIM;
    254    }
    255
    256    public CPMethodOrField getCPIMethod(final String owner, final String name, final String desc) {
    257        return getCPIMethod(getCPClass(owner), name, desc);
    258    }
    259
    260    public CPMethodOrField getCPMethod(final CPClass cpClass, final String name, final String desc) {
    261        final String key = cpClass.toString() + ":" + name + ":" + desc;
    262        CPMethodOrField cpM = stringsToCpMethod.get(key);
    263        if (cpM == null) {
    264            final CPNameAndType nAndT = getCPNameAndType(name, desc);
    265            cpM = new CPMethodOrField(cpClass, nAndT);
    266            cp_Method.add(cpM);
    267            stringsToCpMethod.put(key, cpM);
    268        }
    269        return cpM;
    270    }
    271
    272    public CPMethodOrField getCPMethod(final String owner, final String name, final String desc) {
    273        return getCPMethod(getCPClass(owner), name, desc);
    274    }
    275
    276        public CPNameAndType getCPNameAndType(final String name, final String signature) {
    277        final String descr = name + ":" + signature;
    278        CPNameAndType nameAndType = stringsToCpNameAndType.get(descr);
    279        if (nameAndType == null) {
    280            nameAndType = new CPNameAndType(getCPUtf8(name), getCPSignature(signature));
    281            stringsToCpNameAndType.put(descr, nameAndType);
    282            cp_Descr.add(nameAndType);
    283        }
    284        return nameAndType;
    285    }
    286
    287    public CPSignature getCPSignature(final String signature) {
    288        if (signature == null) {
    289            return null;
    290        }
    291        CPSignature cpS = stringsToCpSignature.get(signature);
    292        if (cpS == null) {
    293            final List<CPClass> cpClasses = new ArrayList<>();
    294            CPUTF8 signatureUTF8;
    295            if (signature.length() > 1 && signature.indexOf('L') != -1) {
    296                final List<String> classes = new ArrayList<>();
    297                final char[] chars = signature.toCharArray();
    298                final StringBuilder signatureString = new StringBuilder();
    299                for (int i = 0; i < chars.length; i++) {
    300                    signatureString.append(chars[i]);
    301                    if (chars[i] == 'L') {
    302                        final StringBuilder className = new StringBuilder();
    303                        for (int j = i + 1; j < chars.length; j++) {
    304                            final char c = chars[j];
    305                            if (!Character.isLetter(c) && !Character.isDigit(c) && (c != '/') && (c != '$')
    306                                && (c != '_')) {
    307                                classes.add(className.toString());
    308                                i = j - 1;
    309                                break;
    310                            }
    311                            className.append(c);
    312                        }
    313                    }
    314                }
    315                removeCpUtf8(signature);
    316                for (String className : classes) {
    317                    CPClass cpClass = null;
    318                    if (className != null) {
    319                        className = className.replace('.', '/');
    320                        cpClass = stringsToCpClass.get(className);
    321                        if (cpClass == null) {
    322                            final CPUTF8 cpUtf8 = getCPUtf8(className);
    323                            cpClass = new CPClass(cpUtf8);
    324                            cp_Class.add(cpClass);
    325                            stringsToCpClass.put(className, cpClass);
    326                        }
    327                    }
    328                    cpClasses.add(cpClass);
    329                }
    330
    331                signatureUTF8 = getCPUtf8(signatureString.toString());
    332            } else {
    333                signatureUTF8 = getCPUtf8(signature);
    334            }
    335            cpS = new CPSignature(signature, signatureUTF8, cpClasses);
    336            cp_Signature.add(cpS);
    337            stringsToCpSignature.put(signature, cpS);
    338        }
    339        return cpS;
    340    }
    341
    342    public CPUTF8 getCPUtf8(final String utf8) {
    343        if (utf8 == null) {
    344            return null;
    345        }
    346        CPUTF8 cpUtf8 = stringsToCpUtf8.get(utf8);
    347        if (cpUtf8 == null) {
    348            cpUtf8 = new CPUTF8(utf8);
    349            cp_Utf8.add(cpUtf8);
    350            stringsToCpUtf8.put(utf8, cpUtf8);
    351        }
    352        return cpUtf8;
    353    }
    354
    355    @Override
    356    public void pack(final OutputStream out) throws IOException, Pack200Exception {
    357        PackingUtils.log("Writing constant pool bands...");
    358        writeCpUtf8(out);
    359        writeCpInt(out);
    360        writeCpFloat(out);
    361        writeCpLong(out);
    362        writeCpDouble(out);
    363        writeCpString(out);
    364        writeCpClass(out);
    365        writeCpSignature(out);
    366        writeCpDescr(out);
    367        writeCpMethodOrField(cp_Field, out, "cp_Field");
    368        writeCpMethodOrField(cp_Method, out, "cp_Method");
    369        writeCpMethodOrField(cp_Imethod, out, "cp_Imethod");
    370    }
    371
    372    private void removeCpUtf8(final String string) {
    373        final CPUTF8 utf8 = stringsToCpUtf8.get(string);
    374        if ((utf8 != null) && (stringsToCpClass.get(string) == null)) { // don't remove if strings are also in cpclass
    375            stringsToCpUtf8.remove(string);
    376            cp_Utf8.remove(utf8);
    377        }
    378    }
    379
    380    private void removeSignaturesFromCpUTF8() {
    381        cp_Signature.forEach(signature -> {
    382            final String sigStr = signature.getUnderlyingString();
    383            final CPUTF8 utf8 = signature.getSignatureForm();
    384            final String form = utf8.getUnderlyingString();
    385            if (!sigStr.equals(form)) {
    386                removeCpUtf8(sigStr);
    387            }
    388        });
    389    }
    390
    391    private void writeCpClass(final OutputStream out) throws IOException, Pack200Exception {
    392        PackingUtils.log("Writing " + cp_Class.size() + " Class entries...");
    393        final int[] cpClass = new int[cp_Class.size()];
    394        int i = 0;
    395        for (final CPClass cpCl : cp_Class) {
    396            cpClass[i] = cpCl.getIndexInCpUtf8();
    397            i++;
    398        }
    399        final byte[] encodedBand = encodeBandInt("cpClass", cpClass, Codec.UDELTA5);
    400        out.write(encodedBand);
    401        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpClass[" + cpClass.length + "]");
    402    }
    403
    404    private void writeCpDescr(final OutputStream out) throws IOException, Pack200Exception {
    405        PackingUtils.log("Writing " + cp_Descr.size() + " Descriptor entries...");
    406        final int[] cpDescrName = new int[cp_Descr.size()];
    407        final int[] cpDescrType = new int[cp_Descr.size()];
    408        int i = 0;
    409        for (final CPNameAndType nameAndType : cp_Descr) {
    410            cpDescrName[i] = nameAndType.getNameIndex();
    411            cpDescrType[i] = nameAndType.getTypeIndex();
    412            i++;
    413        }
    414
    415        byte[] encodedBand = encodeBandInt("cp_Descr_Name", cpDescrName, Codec.DELTA5);
    416        out.write(encodedBand);
    417        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Descr_Name[" + cpDescrName.length + "]");
    418
    419        encodedBand = encodeBandInt("cp_Descr_Type", cpDescrType, Codec.UDELTA5);
    420        out.write(encodedBand);
    421        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Descr_Type[" + cpDescrType.length + "]");
    422    }
    423
    424    private void writeCpDouble(final OutputStream out) throws IOException, Pack200Exception {
    425        PackingUtils.log("Writing " + cp_Double.size() + " Double entries...");
    426        final int[] highBits = new int[cp_Double.size()];
    427        final int[] loBits = new int[cp_Double.size()];
    428        int i = 0;
    429        for (final CPDouble dbl : cp_Double) {
    430            final long l = Double.doubleToLongBits(dbl.getDouble());
    431            highBits[i] = (int) (l >> 32);
    432            loBits[i] = (int) l;
    433            i++;
    434        }
    435        byte[] encodedBand = encodeBandInt("cp_Double_hi", highBits, Codec.UDELTA5);
    436        out.write(encodedBand);
    437        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Double_hi[" + highBits.length + "]");
    438
    439        encodedBand = encodeBandInt("cp_Double_lo", loBits, Codec.DELTA5);
    440        out.write(encodedBand);
    441        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Double_lo[" + loBits.length + "]");
    442    }
    443
    444    private void writeCpFloat(final OutputStream out) throws IOException, Pack200Exception {
    445        PackingUtils.log("Writing " + cp_Float.size() + " Float entries...");
    446        final int[] cpFloat = new int[cp_Float.size()];
    447        int i = 0;
    448        for (final CPFloat fl : cp_Float) {
    449            cpFloat[i] = Float.floatToIntBits(fl.getFloat());
    450            i++;
    451        }
    452        final byte[] encodedBand = encodeBandInt("cp_Float", cpFloat, Codec.UDELTA5);
    453        out.write(encodedBand);
    454        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Float[" + cpFloat.length + "]");
    455    }
    456
    457    private void writeCpInt(final OutputStream out) throws IOException, Pack200Exception {
    458        PackingUtils.log("Writing " + cp_Int.size() + " Integer entries...");
    459        final int[] cpInt = new int[cp_Int.size()];
    460        int i = 0;
    461        for (final CPInt integer : cp_Int) {
    462            cpInt[i] = integer.getInt();
    463            i++;
    464        }
    465        final byte[] encodedBand = encodeBandInt("cp_Int", cpInt, Codec.UDELTA5);
    466        out.write(encodedBand);
    467        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Int[" + cpInt.length + "]");
    468    }
    469
    470    private void writeCpLong(final OutputStream out) throws IOException, Pack200Exception {
    471        PackingUtils.log("Writing " + cp_Long.size() + " Long entries...");
    472        final int[] highBits = new int[cp_Long.size()];
    473        final int[] loBits = new int[cp_Long.size()];
    474        int i = 0;
    475        for (final CPLong lng : cp_Long) {
    476            final long l = lng.getLong();
    477            highBits[i] = (int) (l >> 32);
    478            loBits[i] = (int) l;
    479            i++;
    480        }
    481        byte[] encodedBand = encodeBandInt("cp_Long_hi", highBits, Codec.UDELTA5);
    482        out.write(encodedBand);
    483        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Long_hi[" + highBits.length + "]");
    484
    485        encodedBand = encodeBandInt("cp_Long_lo", loBits, Codec.DELTA5);
    486        out.write(encodedBand);
    487        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cp_Long_lo[" + loBits.length + "]");
    488    }
    489
    490    private void writeCpMethodOrField(final Set<CPMethodOrField> cp, final OutputStream out, final String name)
    491        throws IOException, Pack200Exception {
    492        PackingUtils.log("Writing " + cp.size() + " Method and Field entries...");
    493        final int[] cp_methodOrField_class = new int[cp.size()];
    494        final int[] cp_methodOrField_desc = new int[cp.size()];
    495        int i = 0;
    496        for (final CPMethodOrField mOrF : cp) {
    497            cp_methodOrField_class[i] = mOrF.getClassIndex();
    498            cp_methodOrField_desc[i] = mOrF.getDescIndex();
    499            i++;
    500        }
    501        byte[] encodedBand = encodeBandInt(name + "_class", cp_methodOrField_class, Codec.DELTA5);
    502        out.write(encodedBand);
    503        PackingUtils.log(
    504            "Wrote " + encodedBand.length + " bytes from " + name + "_class[" + cp_methodOrField_class.length + "]");
    505
    506        encodedBand = encodeBandInt(name + "_desc", cp_methodOrField_desc, Codec.UDELTA5);
    507        out.write(encodedBand);
    508        PackingUtils
    509            .log("Wrote " + encodedBand.length + " bytes from " + name + "_desc[" + cp_methodOrField_desc.length + "]");
    510    }
    511
    512    private void writeCpSignature(final OutputStream out) throws IOException, Pack200Exception {
    513        PackingUtils.log("Writing " + cp_Signature.size() + " Signature entries...");
    514        final int[] cpSignatureForm = new int[cp_Signature.size()];
    515        final List<CPClass> classes = new ArrayList<>();
    516        int i = 0;
    517        for (final CPSignature cpS : cp_Signature) {
    518            classes.addAll(cpS.getClasses());
    519            cpSignatureForm[i] = cpS.getIndexInCpUtf8();
    520            i++;
    521        }
    522        final int[] cpSignatureClasses = new int[classes.size()];
    523        Arrays.setAll(cpSignatureClasses, j -> classes.get(j).getIndex());
    524
    525        byte[] encodedBand = encodeBandInt("cpSignatureForm", cpSignatureForm, Codec.DELTA5);
    526        out.write(encodedBand);
    527        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpSignatureForm[" + cpSignatureForm.length + "]");
    528
    529        encodedBand = encodeBandInt("cpSignatureClasses", cpSignatureClasses, Codec.UDELTA5);
    530        out.write(encodedBand);
    531        PackingUtils
    532            .log("Wrote " + encodedBand.length + " bytes from cpSignatureClasses[" + cpSignatureClasses.length + "]");
    533    }
    534
    535    private void writeCpString(final OutputStream out) throws IOException, Pack200Exception {
    536        PackingUtils.log("Writing " + cp_String.size() + " String entries...");
    537        final int[] cpString = new int[cp_String.size()];
    538        int i = 0;
    539        for (final CPString cpStr : cp_String) {
    540            cpString[i] = cpStr.getIndexInCpUtf8();
    541            i++;
    542        }
    543        final byte[] encodedBand = encodeBandInt("cpString", cpString, Codec.UDELTA5);
    544        out.write(encodedBand);
    545        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpString[" + cpString.length + "]");
    546    }
    547
    548    private void writeCpUtf8(final OutputStream out) throws IOException, Pack200Exception {
    549        PackingUtils.log("Writing " + cp_Utf8.size() + " UTF8 entries...");
    550        final int[] cpUtf8Prefix = new int[cp_Utf8.size() - 2];
    551        final int[] cpUtf8Suffix = new int[cp_Utf8.size() - 1];
    552        final List<Character> chars = new ArrayList<>();
    553        final List<Integer> bigSuffix = new ArrayList<>();
    554        final List<Character> bigChars = new ArrayList<>();
    555        final Object[] cpUtf8Array = cp_Utf8.toArray();
    556        final String first = ((CPUTF8) cpUtf8Array[1]).getUnderlyingString();
    557        cpUtf8Suffix[0] = first.length();
    558        addCharacters(chars, first.toCharArray());
    559        for (int i = 2; i < cpUtf8Array.length; i++) {
    560            final char[] previous = ((CPUTF8) cpUtf8Array[i - 1]).getUnderlyingString().toCharArray();
    561            String currentStr = ((CPUTF8) cpUtf8Array[i]).getUnderlyingString();
    562            final char[] current = currentStr.toCharArray();
    563            int prefix = 0;
    564            for (int j = 0; j < previous.length; j++) {
    565                if (previous[j] != current[j]) {
    566                    break;
    567                }
    568                prefix++;
    569            }
    570            cpUtf8Prefix[i - 2] = prefix;
    571            currentStr = currentStr.substring(prefix);
    572            final char[] suffix = currentStr.toCharArray();
    573            if (suffix.length > 1000) { // big suffix (1000 is arbitrary - can we
    574                // do better?)
    575                cpUtf8Suffix[i - 1] = 0;
    576                bigSuffix.add(Integer.valueOf(suffix.length));
    577                addCharacters(bigChars, suffix);
    578            } else {
    579                cpUtf8Suffix[i - 1] = suffix.length;
    580                addCharacters(chars, suffix);
    581            }
    582        }
    583        final int[] cpUtf8Chars = new int[chars.size()];
    584        final int[] cpUtf8BigSuffix = new int[bigSuffix.size()];
    585        final int[][] cpUtf8BigChars = new int[bigSuffix.size()][];
    586        Arrays.setAll(cpUtf8Chars, i -> chars.get(i).charValue());
    587        for (int i = 0; i < cpUtf8BigSuffix.length; i++) {
    588            final int numBigChars = bigSuffix.get(i).intValue();
    589            cpUtf8BigSuffix[i] = numBigChars;
    590            cpUtf8BigChars[i] = new int[numBigChars];
    591            Arrays.setAll(cpUtf8BigChars[i], j -> bigChars.remove(0).charValue());
    592        }
    593
    594        byte[] encodedBand = encodeBandInt("cpUtf8Prefix", cpUtf8Prefix, Codec.DELTA5);
    595        out.write(encodedBand);
    596        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpUtf8Prefix[" + cpUtf8Prefix.length + "]");
    597
    598        encodedBand = encodeBandInt("cpUtf8Suffix", cpUtf8Suffix, Codec.UNSIGNED5);
    599        out.write(encodedBand);
    600        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpUtf8Suffix[" + cpUtf8Suffix.length + "]");
    601
    602        encodedBand = encodeBandInt("cpUtf8Chars", cpUtf8Chars, Codec.CHAR3);
    603        out.write(encodedBand);
    604        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpUtf8Chars[" + cpUtf8Chars.length + "]");
    605
    606        encodedBand = encodeBandInt("cpUtf8BigSuffix", cpUtf8BigSuffix, Codec.DELTA5);
    607        out.write(encodedBand);
    608        PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpUtf8BigSuffix[" + cpUtf8BigSuffix.length + "]");
    609
    610        for (int i = 0; i < cpUtf8BigChars.length; i++) {
    611            encodedBand = encodeBandInt("cpUtf8BigChars " + i, cpUtf8BigChars[i], Codec.DELTA5);
    612            out.write(encodedBand);
    613            PackingUtils.log("Wrote " + encodedBand.length + " bytes from cpUtf8BigChars" + i + "["
    614                + cpUtf8BigChars[i].length + "]");
    615        }
    616    }
    617
    618}