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 */ 019package org.apache.commons.compress.harmony.unpack200; 020 021import java.io.IOException; 022import java.io.InputStream; 023import java.util.Arrays; 024 025import org.apache.commons.compress.harmony.pack200.BHSDCodec; 026import org.apache.commons.compress.harmony.pack200.Codec; 027import org.apache.commons.compress.harmony.pack200.CodecEncoding; 028import org.apache.commons.compress.harmony.pack200.Pack200Exception; 029import org.apache.commons.compress.harmony.pack200.PopulationCodec; 030import org.apache.commons.compress.harmony.unpack200.bytecode.CPClass; 031import org.apache.commons.compress.harmony.unpack200.bytecode.CPDouble; 032import org.apache.commons.compress.harmony.unpack200.bytecode.CPFieldRef; 033import org.apache.commons.compress.harmony.unpack200.bytecode.CPFloat; 034import org.apache.commons.compress.harmony.unpack200.bytecode.CPInteger; 035import org.apache.commons.compress.harmony.unpack200.bytecode.CPInterfaceMethodRef; 036import org.apache.commons.compress.harmony.unpack200.bytecode.CPLong; 037import org.apache.commons.compress.harmony.unpack200.bytecode.CPMethodRef; 038import org.apache.commons.compress.harmony.unpack200.bytecode.CPNameAndType; 039import org.apache.commons.compress.harmony.unpack200.bytecode.CPString; 040import org.apache.commons.compress.harmony.unpack200.bytecode.CPUTF8; 041import org.apache.commons.compress.utils.ExactMath; 042import org.apache.commons.lang3.ArrayUtils; 043 044/** 045 * Abstract superclass for a set of bands. 046 */ 047public abstract class BandSet { 048 049 /** 050 * Segment. 051 */ 052 protected Segment segment; 053 054 /** 055 * Segment header. 056 */ 057 protected SegmentHeader header; 058 059 /** 060 * Constructs a new instance for the given segment. 061 * 062 * @param segment The segment. 063 */ 064 public BandSet(final Segment segment) { 065 this.segment = segment; 066 this.header = segment.getSegmentHeader(); 067 } 068 069 /** 070 * Decodes a band and return an array of {@code int} values. 071 * 072 * @param name the name of the band (for logging and debugging). 073 * @param in the InputStream to decode. 074 * @param codec the default Codec for this band. 075 * @param count the number of elements to read. 076 * @return an array of decoded {@code int} values. 077 * @throws IOException if there is a problem reading from the underlying input stream. 078 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid. 079 */ 080 public int[] decodeBandInt(final String name, final InputStream in, final BHSDCodec codec, final int count) throws IOException, Pack200Exception { 081 if (count < 0) { 082 throw new Pack200Exception("count < 0"); 083 } 084 // Useful for debugging 085// if (count > 0) { 086// System.out.println("decoding " + name + " " + count); 087// } 088 Codec codecUsed = codec; 089 if (codec.getB() == 1 || count == 0) { 090 return codec.decodeInts(count, in); 091 } 092 final int[] getFirst = codec.decodeInts(1, in); 093 if (getFirst.length == 0) { 094 return getFirst; 095 } 096 final int first = getFirst[0]; 097 final int[] band; 098 if (codec.isSigned() && first >= -256 && first <= -1) { 099 // Non-default codec should be used 100 codecUsed = CodecEncoding.getCodec(-1 - first, header.getBandHeadersInputStream(), codec); 101 band = codecUsed.decodeInts(count, in); 102 } else if (!codec.isSigned() && first >= codec.getL() && first <= codec.getL() + 255) { 103 // Non-default codec should be used 104 codecUsed = CodecEncoding.getCodec(first - codec.getL(), header.getBandHeadersInputStream(), codec); 105 band = codecUsed.decodeInts(count, in); 106 } else { 107 // First element should not be discarded 108 band = codec.decodeInts(count - 1, in, first); 109 } 110 // Useful for debugging -E options: 111 // if (!codecUsed.equals(codec)) { 112 // int bytes = codecUsed.lastBandLength; 113 // System.out.println(count + " " + name + " encoded with " + codecUsed + " " + bytes); 114 // } 115 if (codecUsed instanceof PopulationCodec) { 116 final PopulationCodec popCodec = (PopulationCodec) codecUsed; 117 final int[] favoured = popCodec.getFavoured().clone(); 118 Arrays.sort(favoured); 119 for (int i = 0; i < band.length; i++) { 120 final boolean favouredValue = Arrays.binarySearch(favoured, band[i]) > -1; 121 final Codec theCodec = favouredValue ? popCodec.getFavouredCodec() : popCodec.getUnfavouredCodec(); 122 if (theCodec instanceof BHSDCodec && ((BHSDCodec) theCodec).isDelta()) { 123 final BHSDCodec bhsd = (BHSDCodec) theCodec; 124 final long cardinality = bhsd.cardinality(); 125 while (band[i] > bhsd.largest()) { 126 band[i] -= cardinality; 127 } 128 while (band[i] < bhsd.smallest()) { 129 band[i] = ExactMath.add(band[i], cardinality); 130 } 131 } 132 } 133 } 134 return band; 135 } 136 137 /** 138 * Decodes a band and return an array of {@code int[]} values. 139 * 140 * @param name the name of the band (primarily for logging/debugging purposes) 141 * @param in the InputStream to decode from 142 * @param defaultCodec the default codec for this band 143 * @param counts the numbers of elements to read for each int array within the array to be returned 144 * @return an array of decoded {@code int[]} values 145 * @throws IOException if there is a problem reading from the underlying input stream 146 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid 147 */ 148 public int[][] decodeBandInt(final String name, final InputStream in, final BHSDCodec defaultCodec, final int[] counts) 149 throws IOException, Pack200Exception { 150 final int[][] result = new int[counts.length][]; 151 int totalCount = 0; 152 for (final int count : counts) { 153 totalCount += count; 154 } 155 final int[] twoDResult = decodeBandInt(name, in, defaultCodec, totalCount); 156 int index = 0; 157 for (int i = 0; i < result.length; i++) { 158 if (counts[i] > twoDResult.length) { 159 throw new IOException("Counts value exceeds length of twoDResult"); 160 } 161 result[i] = new int[counts[i]]; 162 for (int j = 0; j < result[i].length; j++) { 163 result[i][j] = twoDResult[index]; 164 index++; 165 } 166 } 167 return result; 168 } 169 170 /** 171 * Gets a new array of String references for the subset defined by the given {@code ints} array. 172 * 173 * @param ints The indices into the {@code reference} array. 174 * @param reference The source array. 175 * @return a new array. 176 */ 177 protected String[] getReferences(final int[] ints, final String[] reference) { 178 return ArrayUtils.setAll(new String[ints.length], i -> reference[ints[i]]); 179 } 180 181 /** 182 * Gets a new array of String references for the subset defined by the given {@code ints} array. 183 * 184 * @param ints The indices into the {@code reference} array. 185 * @param reference The source array. 186 * @return a new array. 187 */ 188 protected String[][] getReferences(final int[][] ints, final String[] reference) { 189 final String[][] result = new String[ints.length][]; 190 for (int i = 0; i < result.length; i++) { 191 result[i] = new String[ints[i].length]; 192 for (int j = 0; j < result[i].length; j++) { 193 result[i][j] = reference[ints[i][j]]; 194 } 195 } 196 return result; 197 } 198 199 /** 200 * Parses an input stream into an array of {@link CPClass}. 201 * 202 * @param name the name of the band (for logging and debugging). 203 * @param in the input stream to parse. 204 * @param codec the default {@link BHSDCodec} for this band 205 * @param count the number of elements to read. 206 * @return an array of decoded {@link CPClass}. 207 * @throws IOException if there is a problem reading from the underlying input stream. 208 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid. 209 */ 210 public CPClass[] parseCPClassReferences(final String name, final InputStream in, final BHSDCodec codec, final int count) 211 throws IOException, Pack200Exception { 212 final int[] indices = decodeBandInt(name, in, codec, count); 213 final CpBands cpBands = segment.getCpBands(); 214 return ArrayUtils.setAll(new CPClass[indices.length], i -> cpBands.cpClassValue(indices[i])); 215 } 216 217 /** 218 * Parses an input stream into an array of {@link CPNameAndType}. 219 * 220 * @param name the name of the band (for logging and debugging). 221 * @param in the input stream to parse. 222 * @param codec the default {@link BHSDCodec} for this band 223 * @param count the number of elements to read. 224 * @return an array of decoded {@link CPNameAndType}. 225 * @throws IOException if there is a problem reading from the underlying input stream. 226 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid. 227 */ 228 public CPNameAndType[] parseCPDescriptorReferences(final String name, final InputStream in, final BHSDCodec codec, final int count) 229 throws IOException, Pack200Exception { 230 final int[] indices = decodeBandInt(name, in, codec, count); 231 final CpBands cpBands = segment.getCpBands(); 232 return ArrayUtils.setAll(new CPNameAndType[indices.length], i -> cpBands.cpNameAndTypeValue(indices[i])); 233 } 234 235 /** 236 * Parses an input stream into an array of {@link CPDouble}. 237 * 238 * @param name the name of the band (for logging and debugging). 239 * @param in the input stream to parse. 240 * @param codec the default {@link BHSDCodec} for this band 241 * @param count the number of elements to read. 242 * @return an array of decoded {@link CPDouble}. 243 * @throws IOException if there is a problem reading from the underlying input stream. 244 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid. 245 */ 246 public CPDouble[] parseCPDoubleReferences(final String name, final InputStream in, final BHSDCodec codec, final int count) 247 throws IOException, Pack200Exception { 248 final int[] indices = decodeBandInt(name, in, codec, count); 249 final CpBands cpBands = segment.getCpBands(); 250 return ArrayUtils.setAll(new CPDouble[indices.length], i -> cpBands.cpDoubleValue(indices[i])); 251 } 252 253 /** 254 * Parses an input stream into an array of {@link CPFieldRef}. 255 * 256 * @param name the name of the band (for logging and debugging). 257 * @param in the input stream to parse. 258 * @param codec the default {@link BHSDCodec} for this band 259 * @param count the number of elements to read. 260 * @return an array of decoded {@link CPFieldRef}. 261 * @throws IOException if there is a problem reading from the underlying input stream. 262 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid. 263 */ 264 public CPFieldRef[] parseCPFieldRefReferences(final String name, final InputStream in, final BHSDCodec codec, final int count) 265 throws IOException, Pack200Exception { 266 final int[] indices = decodeBandInt(name, in, codec, count); 267 final CpBands cpBands = segment.getCpBands(); 268 return ArrayUtils.setAll(new CPFieldRef[indices.length], i -> cpBands.cpFieldValue(indices[i])); 269 } 270 271 /** 272 * Parses an input stream into an array of {@link CPFloat}. 273 * 274 * @param name the name of the band (for logging and debugging). 275 * @param in the input stream to parse. 276 * @param codec the default {@link BHSDCodec} for this band 277 * @param count the number of elements to read. 278 * @return an array of decoded {@link CPFloat}. 279 * @throws IOException if there is a problem reading from the underlying input stream. 280 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid. 281 */ 282 public CPFloat[] parseCPFloatReferences(final String name, final InputStream in, final BHSDCodec codec, final int count) 283 throws IOException, Pack200Exception { 284 final int[] indices = decodeBandInt(name, in, codec, count); 285 final CpBands cpBands = segment.getCpBands(); 286 return ArrayUtils.setAll(new CPFloat[indices.length], i -> cpBands.cpFloatValue(indices[i])); 287 } 288 289 /** 290 * Parses an input stream into an array of {@link CPInterfaceMethodRef}. 291 * 292 * @param name the name of the band (for logging and debugging). 293 * @param in the input stream to parse. 294 * @param codec the default {@link BHSDCodec} for this band 295 * @param count the number of elements to read. 296 * @return an array of decoded {@link CPInterfaceMethodRef}. 297 * @throws IOException if there is a problem reading from the underlying input stream. 298 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid. 299 */ 300 public CPInterfaceMethodRef[] parseCPInterfaceMethodRefReferences(final String name, final InputStream in, final BHSDCodec codec, final int count) 301 throws IOException, Pack200Exception { 302 final int[] indices = decodeBandInt(name, in, codec, count); 303 final CpBands cpBands = segment.getCpBands(); 304 return ArrayUtils.setAll(new CPInterfaceMethodRef[indices.length], i -> cpBands.cpIMethodValue(indices[i])); 305 } 306 307 /** 308 * Parses an input stream into an array of {@link CPInteger}. 309 * 310 * @param name the name of the band (for logging and debugging). 311 * @param in the input stream to parse. 312 * @param codec the default {@link BHSDCodec} for this band 313 * @param count the number of elements to read. 314 * @return an array of decoded {@link CPInteger}. 315 * @throws IOException if there is a problem reading from the underlying input stream. 316 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid. 317 */ 318 public CPInteger[] parseCPIntReferences(final String name, final InputStream in, final BHSDCodec codec, final int count) 319 throws IOException, Pack200Exception { 320 final CpBands cpBands = segment.getCpBands(); 321 final int[] reference = cpBands.getCpInt(); 322 final int[] indices = decodeBandInt(name, in, codec, count); 323 final CPInteger[] result = new CPInteger[indices.length]; 324 for (int i = 0; i < count; i++) { 325 final int index = indices[i]; 326 if (index < 0 || index >= reference.length) { 327 throw new Pack200Exception("Something has gone wrong during parsing references, index = " + index + ", array size = " + reference.length); 328 } 329 result[i] = cpBands.cpIntegerValue(index); 330 } 331 return result; 332 } 333 334 /** 335 * Parses an input stream into an array of {@link CPLong}. 336 * 337 * @param name the name of the band (for logging and debugging). 338 * @param in the input stream to parse. 339 * @param codec the default {@link BHSDCodec} for this band 340 * @param count the number of elements to read. 341 * @return an array of decoded {@link CPLong}. 342 * @throws IOException if there is a problem reading from the underlying input stream. 343 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid. 344 */ 345 public CPLong[] parseCPLongReferences(final String name, final InputStream in, final BHSDCodec codec, final int count) 346 throws IOException, Pack200Exception { 347 final CpBands cpBands = segment.getCpBands(); 348 final long[] reference = cpBands.getCpLong(); 349 final int[] indices = decodeBandInt(name, in, codec, count); 350 final CPLong[] result = new CPLong[indices.length]; 351 for (int i = 0; i < count; i++) { 352 final int index = indices[i]; 353 if (index < 0 || index >= reference.length) { 354 throw new Pack200Exception("Something has gone wrong during parsing references, index = " + index + ", array size = " + reference.length); 355 } 356 result[i] = cpBands.cpLongValue(index); 357 } 358 return result; 359 } 360 361 /** 362 * Parses an input stream into an array of {@link CPMethodRef}. 363 * 364 * @param name the name of the band (for logging and debugging). 365 * @param in the input stream to parse. 366 * @param codec the default {@link BHSDCodec} for this band 367 * @param count the number of elements to read. 368 * @return an array of decoded {@link CPMethodRef}. 369 * @throws IOException if there is a problem reading from the underlying input stream. 370 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid. 371 */ 372 public CPMethodRef[] parseCPMethodRefReferences(final String name, final InputStream in, final BHSDCodec codec, final int count) 373 throws IOException, Pack200Exception { 374 final int[] indices = decodeBandInt(name, in, codec, count); 375 final CpBands cpBands = segment.getCpBands(); 376 return ArrayUtils.setAll(new CPMethodRef[indices.length], i -> cpBands.cpMethodValue(indices[i])); 377 } 378 379 /** 380 * Parses an input stream into an array of {@link CPUTF8}. 381 * 382 * @param name the name of the band (for logging and debugging). 383 * @param in the input stream to parse. 384 * @param codec the default {@link BHSDCodec} for this band 385 * @param count the number of elements to read. 386 * @return an array of decoded {@link CPUTF8}. 387 * @throws IOException if there is a problem reading from the underlying input stream. 388 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid. 389 */ 390 public CPUTF8[] parseCPSignatureReferences(final String name, final InputStream in, final BHSDCodec codec, final int count) 391 throws IOException, Pack200Exception { 392 final int[] indices = decodeBandInt(name, in, codec, count); 393 final CpBands cpBands = segment.getCpBands(); 394 return ArrayUtils.setAll(new CPUTF8[indices.length], i -> cpBands.cpSignatureValue(indices[i])); 395 } 396 397 /** 398 * Parses an input stream into an array of array of {@link CPUTF8}. 399 * 400 * @param name the name of the band (for logging and debugging). 401 * @param in the input stream to parse. 402 * @param codec the default {@link BHSDCodec} for this band 403 * @param counts the number of elements to read. 404 * @return an array of array of decoded {@link CPUTF8}. 405 * @throws IOException if there is a problem reading from the underlying input stream. 406 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid. 407 */ 408 protected CPUTF8[][] parseCPSignatureReferences(final String name, final InputStream in, final BHSDCodec codec, final int[] counts) 409 throws IOException, Pack200Exception { 410 int sum = 0; 411 for (final int count : counts) { 412 sum += count; 413 } 414 final int[] indices = decodeBandInt(name, in, codec, sum); 415 final CpBands cpBands = segment.getCpBands(); 416 final CPUTF8[] result1 = ArrayUtils.setAll(new CPUTF8[sum], i -> cpBands.cpSignatureValue(indices[i])); 417 int pos = 0; 418 final CPUTF8[][] result = new CPUTF8[counts.length][]; 419 for (int i = 0; i < counts.length; i++) { 420 final int num = counts[i]; 421 result[i] = new CPUTF8[num]; 422 System.arraycopy(result1, pos, result[i], 0, num); 423 pos += num; 424 } 425 return result; 426 } 427 428 /** 429 * Parses an input stream into an array of {@link CPString}. 430 * 431 * @param name the name of the band (for logging and debugging). 432 * @param in the input stream to parse. 433 * @param codec the default {@link BHSDCodec} for this band 434 * @param count the number of elements to read. 435 * @return an array of decoded {@link CPString}. 436 * @throws IOException if there is a problem reading from the underlying input stream. 437 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid. 438 */ 439 public CPString[] parseCPStringReferences(final String name, final InputStream in, final BHSDCodec codec, final int count) 440 throws IOException, Pack200Exception { 441 final int[] indices = decodeBandInt(name, in, codec, count); 442 final CpBands cpBands = segment.getCpBands(); 443 return ArrayUtils.setAll(new CPString[indices.length], i -> cpBands.cpStringValue(indices[i])); 444 } 445 446 /** 447 * Parses an input stream into an array of {@link CPUTF8}. 448 * 449 * @param name the name of the band (for logging and debugging). 450 * @param in the input stream to parse. 451 * @param codec the default {@link BHSDCodec} for this band 452 * @param count the number of elements to read. 453 * @return an array of decoded {@link CPUTF8}. 454 * @throws IOException if there is a problem reading from the underlying input stream. 455 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid. 456 */ 457 public CPUTF8[] parseCPUTF8References(final String name, final InputStream in, final BHSDCodec codec, final int count) 458 throws IOException, Pack200Exception { 459 final int[] indices = decodeBandInt(name, in, codec, count); 460 final CpBands cpBands = segment.getCpBands(); 461 return ArrayUtils.setAll(new CPUTF8[indices.length], i -> cpBands.cpUTF8Value(indices[i])); 462 } 463 464 /** 465 * Parses an input stream into an array of array of {@link CPUTF8}. 466 * 467 * @param name the name of the band (for logging and debugging). 468 * @param in the input stream to parse. 469 * @param codec the default {@link BHSDCodec} for this band 470 * @param counts the number of elements to read. 471 * @return an array of array of decoded {@link CPUTF8}. 472 * @throws IOException if there is a problem reading from the underlying input stream. 473 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid. 474 */ 475 public CPUTF8[][] parseCPUTF8References(final String name, final InputStream in, final BHSDCodec codec, final int[] counts) 476 throws IOException, Pack200Exception { 477 final CPUTF8[][] result = new CPUTF8[counts.length][]; 478 int sum = 0; 479 for (int i = 0; i < counts.length; i++) { 480 result[i] = new CPUTF8[counts[i]]; 481 sum += counts[i]; 482 } 483 final int[] indices = decodeBandInt(name, in, codec, sum); 484 final CpBands cpBands = segment.getCpBands(); 485 final CPUTF8[] result1 = ArrayUtils.setAll(new CPUTF8[sum], i -> cpBands.cpUTF8Value(indices[i])); 486 int pos = 0; 487 for (int i = 0; i < counts.length; i++) { 488 final int num = counts[i]; 489 result[i] = new CPUTF8[num]; 490 System.arraycopy(result1, pos, result[i], 0, num); 491 pos += num; 492 } 493 return result; 494 } 495 496 /** 497 * Parses an input stream into an array of flags. 498 * 499 * @param name the name of the band (for logging and debugging). 500 * @param in the InputStream to decode. 501 * @param count the number of elements to read. 502 * @param hiCodec an optional codec if the high flag is on. 503 * @param loCodec the codec. 504 * @return an array of decoded {@code long} flags. 505 * @throws IOException if there is a problem reading from the underlying input stream. 506 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid. 507 */ 508 public long[] parseFlags(final String name, final InputStream in, final int count, final BHSDCodec hiCodec, final BHSDCodec loCodec) 509 throws IOException, Pack200Exception { 510 return parseFlags(name, in, new int[] { count }, hiCodec, loCodec)[0]; 511 } 512 513 /** 514 * Parses an input stream into an array of flags. 515 * 516 * @param name the name of the band (for logging and debugging). 517 * @param in the InputStream to decode. 518 * @param count the number of elements to read. 519 * @param codec the codec. 520 * @param hasHi whether to the high flag is enabled. 521 * @return an array of decoded {@code long} flags. 522 * @throws IOException if there is a problem reading from the underlying input stream. 523 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid. 524 */ 525 public long[] parseFlags(final String name, final InputStream in, final int count, final BHSDCodec codec, final boolean hasHi) 526 throws IOException, Pack200Exception { 527 return parseFlags(name, in, new int[] { count }, hasHi ? codec : null, codec)[0]; 528 } 529 530 /** 531 * Parses an input stream into an array of flags. 532 * 533 * @param name the name of the band (for logging and debugging). 534 * @param in the InputStream to decode. 535 * @param counts the number of elements to read. 536 * @param hiCodec an optional codec if the high flag is on. 537 * @param loCodec the codec. 538 * @return an array of decoded {@code long} flags. 539 * @throws IOException if there is a problem reading from the underlying input stream. 540 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid. 541 */ 542 public long[][] parseFlags(final String name, final InputStream in, final int[] counts, final BHSDCodec hiCodec, final BHSDCodec loCodec) 543 throws IOException, Pack200Exception { 544 final int count = counts.length; 545 if (count == 0) { 546 return new long[][] { {} }; 547 } 548 int sum = 0; 549 final long[][] result = new long[count][]; 550 for (int i = 0; i < count; i++) { 551 sum += counts[i]; 552 } 553 int[] hi = null; 554 final int[] lo; 555 if (hiCodec != null) { 556 hi = decodeBandInt(name, in, hiCodec, sum); 557 } 558 lo = decodeBandInt(name, in, loCodec, sum); 559 560 int index = 0; 561 for (int i = 0; i < count; i++) { 562 result[i] = new long[counts[i]]; 563 for (int j = 0; j < result[i].length; j++) { 564 if (hi != null) { 565 result[i][j] = (long) hi[index] << 32 | lo[index] & 4294967295L; 566 } else { 567 result[i][j] = lo[index]; 568 } 569 index++; 570 } 571 } 572 return result; 573 } 574 575 /** 576 * Parses an input stream into an array of flags. 577 * 578 * @param name the name of the band (for logging and debugging). 579 * @param in the InputStream to decode. 580 * @param counts the number of elements to read. 581 * @param codec the codec. 582 * @param hasHi whether the high flag is enabnled. 583 * @return an array of decoded {@code long} flags. 584 * @throws IOException if there is a problem reading from the underlying input stream. 585 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid. 586 */ 587 public long[][] parseFlags(final String name, final InputStream in, final int[] counts, final BHSDCodec codec, final boolean hasHi) 588 throws IOException, Pack200Exception { 589 return parseFlags(name, in, counts, hasHi ? codec : null, codec); 590 } 591 592 /** 593 * Parses <em>count</em> references from {@code in}, using {@code codec} to decode the values as indexes into {@code reference} (which is populated prior to 594 * this call). An exception is thrown if a decoded index falls outside the range [0..reference.length-1]. 595 * 596 * @param name the band name 597 * @param in the input stream to read from 598 * @param codec the BHSDCodec to use for decoding 599 * @param count the number of references to decode 600 * @param reference the array of values to use for the references 601 * @return Parsed references. 602 * @throws IOException if a problem occurs during reading from the underlying stream. 603 * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported Codec. 604 */ 605 public String[] parseReferences(final String name, final InputStream in, final BHSDCodec codec, final int count, final String[] reference) 606 throws IOException, Pack200Exception { 607 return parseReferences(name, in, codec, new int[] { count }, reference)[0]; 608 } 609 610 /** 611 * Parses <em>count</em> references from {@code in}, using {@code codec} to decode the values as indexes into {@code reference} (which is populated prior to 612 * this call). An exception is thrown if a decoded index falls outside the range [0..reference.length-1]. Unlike the other parseReferences, this 613 * post-processes the result into an array of results. 614 * 615 * @param name the name of the band (for logging and debugging). 616 * @param in the input stream to read from. 617 * @param codec the BHSDCodec to use for decoding. 618 * @param counts the numbers of references to decode for each array entry. 619 * @param reference the array of values to use for the references. 620 * @return Parsed references. 621 * @throws IOException if a problem occurs during reading from the underlying stream. 622 * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported Codec. 623 */ 624 public String[][] parseReferences(final String name, final InputStream in, final BHSDCodec codec, final int[] counts, final String[] reference) 625 throws IOException, Pack200Exception { 626 final int count = counts.length; 627 if (count == 0) { 628 return new String[][] { {} }; 629 } 630 int sum = 0; 631 for (int i = 0; i < count; i++) { 632 sum += counts[i]; 633 } 634 // TODO Merge the decode and parsing of a multiple structure into one 635 final int[] indices = decodeBandInt(name, in, codec, sum); 636 final String[] result1 = new String[sum]; 637 for (int i1 = 0; i1 < sum; i1++) { 638 final int index = indices[i1]; 639 if (index < 0 || index >= reference.length) { 640 throw new Pack200Exception("Something has gone wrong during parsing references, index = " + index + ", array size = " + reference.length); 641 } 642 result1[i1] = reference[index]; 643 } 644 // TODO Merge the decode and parsing of a multiple structure into one 645 final String[][] result = new String[count][]; 646 int pos = 0; 647 for (int i = 0; i < count; i++) { 648 final int num = counts[i]; 649 result[i] = new String[num]; 650 System.arraycopy(result1, pos, result[i], 0, num); 651 pos += num; 652 } 653 return result; 654 } 655 656 /** 657 * Reads the input stream. 658 * 659 * @param inputStream the stream to read. 660 * @throws IOException if a problem occurs during reading from the underlying stream. 661 * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported Codec. 662 */ 663 public abstract void read(InputStream inputStream) throws IOException, Pack200Exception; 664 665 /** 666 * Unpacks this instance. 667 * 668 * @throws IOException if a problem occurs during reading from the underlying stream. 669 * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported Codec. 670 */ 671 public abstract void unpack() throws IOException, Pack200Exception; 672 673 /** 674 * Unpacks the input stream. 675 * 676 * @param in the stream to unpack. 677 * @throws IOException if a problem occurs during reading from the underlying stream. 678 * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported Codec. 679 */ 680 public void unpack(final InputStream in) throws IOException, Pack200Exception { 681 read(in); 682 unpack(); 683 } 684 685}