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