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}