001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. 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, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.compress.harmony.unpack200; 018 019import java.io.IOException; 020import java.io.InputStream; 021import java.util.Arrays; 022 023import org.apache.commons.compress.harmony.pack200.BHSDCodec; 024import org.apache.commons.compress.harmony.pack200.Codec; 025import org.apache.commons.compress.harmony.pack200.CodecEncoding; 026import org.apache.commons.compress.harmony.pack200.Pack200Exception; 027import org.apache.commons.compress.harmony.pack200.PopulationCodec; 028import org.apache.commons.compress.harmony.unpack200.bytecode.CPClass; 029import org.apache.commons.compress.harmony.unpack200.bytecode.CPDouble; 030import org.apache.commons.compress.harmony.unpack200.bytecode.CPFieldRef; 031import org.apache.commons.compress.harmony.unpack200.bytecode.CPFloat; 032import org.apache.commons.compress.harmony.unpack200.bytecode.CPInteger; 033import org.apache.commons.compress.harmony.unpack200.bytecode.CPInterfaceMethodRef; 034import org.apache.commons.compress.harmony.unpack200.bytecode.CPLong; 035import org.apache.commons.compress.harmony.unpack200.bytecode.CPMethodRef; 036import org.apache.commons.compress.harmony.unpack200.bytecode.CPNameAndType; 037import org.apache.commons.compress.harmony.unpack200.bytecode.CPString; 038import org.apache.commons.compress.harmony.unpack200.bytecode.CPUTF8; 039import org.apache.commons.compress.utils.ExactMath; 040 041/** 042 * Abstract superclass for a set of bands 043 */ 044public abstract class BandSet { 045 046 protected Segment segment; 047 048 protected SegmentHeader header; 049 050 public BandSet(final Segment segment) { 051 this.segment = segment; 052 this.header = segment.getSegmentHeader(); 053 } 054 055 /** 056 * Decode a band and return an array of {@code int} values 057 * 058 * @param name the name of the band (primarily for logging/debugging purposes) 059 * @param in the InputStream to decode from 060 * @param codec the default Codec for this band 061 * @param count the number of elements to read 062 * @return an array of decoded {@code int} values 063 * @throws IOException if there is a problem reading from the underlying input stream 064 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid 065 */ 066 public int[] decodeBandInt(final String name, final InputStream in, final BHSDCodec codec, final int count) 067 throws IOException, Pack200Exception { 068 int[] band; 069 // Useful for debugging 070// if (count > 0) { 071// System.out.println("decoding " + name + " " + count); 072// } 073 Codec codecUsed = codec; 074 if (codec.getB() == 1 || count == 0) { 075 return codec.decodeInts(count, in); 076 } 077 final int[] getFirst = codec.decodeInts(1, in); 078 if (getFirst.length == 0) { 079 return getFirst; 080 } 081 final int first = getFirst[0]; 082 if (codec.isSigned() && first >= -256 && first <= -1) { 083 // Non-default codec should be used 084 codecUsed = CodecEncoding.getCodec(-1 - first, header.getBandHeadersInputStream(), codec); 085 band = codecUsed.decodeInts(count, in); 086 } else if (!codec.isSigned() && first >= codec.getL() && first <= codec.getL() + 255) { 087 // Non-default codec should be used 088 codecUsed = CodecEncoding.getCodec(first - codec.getL(), header.getBandHeadersInputStream(), codec); 089 band = codecUsed.decodeInts(count, in); 090 } else { 091 // First element should not be discarded 092 band = codec.decodeInts(count - 1, in, first); 093 } 094 // Useful for debugging -E options: 095 // if (!codecUsed.equals(codec)) { 096 // int bytes = codecUsed.lastBandLength; 097 // System.out.println(count + " " + name + " encoded with " + codecUsed + " " + bytes); 098 // } 099 if (codecUsed instanceof PopulationCodec) { 100 final PopulationCodec popCodec = (PopulationCodec) codecUsed; 101 final int[] favoured = popCodec.getFavoured().clone(); 102 Arrays.sort(favoured); 103 for (int i = 0; i < band.length; i++) { 104 final boolean favouredValue = Arrays.binarySearch(favoured, band[i]) > -1; 105 final Codec theCodec = favouredValue ? popCodec.getFavouredCodec() : popCodec.getUnfavouredCodec(); 106 if (theCodec instanceof BHSDCodec && ((BHSDCodec) theCodec).isDelta()) { 107 final BHSDCodec bhsd = (BHSDCodec) theCodec; 108 final long cardinality = bhsd.cardinality(); 109 while (band[i] > bhsd.largest()) { 110 band[i] -= cardinality; 111 } 112 while (band[i] < bhsd.smallest()) { 113 band[i] = ExactMath.add(band[i], cardinality); 114 } 115 } 116 } 117 } 118 return band; 119 } 120 121 /** 122 * Decode a band and return an array of {@code int[]} values 123 * 124 * @param name the name of the band (primarily for logging/debugging purposes) 125 * @param in the InputStream to decode from 126 * @param defaultCodec the default codec for this band 127 * @param counts the numbers of elements to read for each int array within the array to be returned 128 * @return an array of decoded {@code int[]} values 129 * @throws IOException if there is a problem reading from the underlying input stream 130 * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid 131 */ 132 public int[][] decodeBandInt(final String name, final InputStream in, final BHSDCodec defaultCodec, 133 final int[] counts) throws IOException, Pack200Exception { 134 final int[][] result = new int[counts.length][]; 135 int totalCount = 0; 136 for (final int count : counts) { 137 totalCount += count; 138 } 139 final int[] twoDResult = decodeBandInt(name, in, defaultCodec, totalCount); 140 int index = 0; 141 for (int i = 0; i < result.length; i++) { 142 result[i] = new int[counts[i]]; 143 for (int j = 0; j < result[i].length; j++) { 144 result[i][j] = twoDResult[index]; 145 index++; 146 } 147 } 148 return result; 149 } 150 151 protected String[] getReferences(final int[] ints, final String[] reference) { 152 final String[] result = new String[ints.length]; 153 Arrays.setAll(result, i -> reference[ints[i]]); 154 return result; 155 } 156 157 protected String[][] getReferences(final int[][] ints, final String[] reference) { 158 final String[][] result = new String[ints.length][]; 159 for (int i = 0; i < result.length; i++) { 160 result[i] = new String[ints[i].length]; 161 for (int j = 0; j < result[i].length; j++) { 162 result[i][j] = reference[ints[i][j]]; 163 } 164 } 165 return result; 166 } 167 168 public CPClass[] parseCPClassReferences(final String name, final InputStream in, final BHSDCodec codec, 169 final int count) throws IOException, Pack200Exception { 170 final int[] indices = decodeBandInt(name, in, codec, count); 171 final CPClass[] result = new CPClass[indices.length]; 172 for (int i1 = 0; i1 < count; i1++) { 173 result[i1] = segment.getCpBands().cpClassValue(indices[i1]); 174 } 175 return result; 176 } 177 178 public CPNameAndType[] parseCPDescriptorReferences(final String name, final InputStream in, final BHSDCodec codec, 179 final int count) throws IOException, Pack200Exception { 180 final CpBands cpBands = segment.getCpBands(); 181 final int[] indices = decodeBandInt(name, in, codec, count); 182 final CPNameAndType[] result = new CPNameAndType[indices.length]; 183 for (int i1 = 0; i1 < count; i1++) { 184 final int index = indices[i1]; 185 result[i1] = cpBands.cpNameAndTypeValue(index); 186 } 187 return result; 188 } 189 190 public CPDouble[] parseCPDoubleReferences(final String name, final InputStream in, final BHSDCodec codec, 191 final int count) throws IOException, Pack200Exception { 192 final int[] indices = decodeBandInt(name, in, codec, count); 193 final CPDouble[] result = new CPDouble[indices.length]; 194 for (int i1 = 0; i1 < count; i1++) { 195 result[i1] = segment.getCpBands().cpDoubleValue(indices[i1]); 196 } 197 return result; 198 } 199 200 public CPFieldRef[] parseCPFieldRefReferences(final String name, final InputStream in, final BHSDCodec codec, 201 final int count) throws IOException, Pack200Exception { 202 final CpBands cpBands = segment.getCpBands(); 203 final int[] indices = decodeBandInt(name, in, codec, count); 204 final CPFieldRef[] result = new CPFieldRef[indices.length]; 205 for (int i1 = 0; i1 < count; i1++) { 206 final int index = indices[i1]; 207 result[i1] = cpBands.cpFieldValue(index); 208 } 209 return result; 210 } 211 212 public CPFloat[] parseCPFloatReferences(final String name, final InputStream in, final BHSDCodec codec, 213 final int count) throws IOException, Pack200Exception { 214 final int[] indices = decodeBandInt(name, in, codec, count); 215 final CPFloat[] result = new CPFloat[indices.length]; 216 for (int i1 = 0; i1 < count; i1++) { 217 result[i1] = segment.getCpBands().cpFloatValue(indices[i1]); 218 } 219 return result; 220 } 221 222 public CPInterfaceMethodRef[] parseCPInterfaceMethodRefReferences(final String name, final InputStream in, 223 final BHSDCodec codec, final int count) throws IOException, Pack200Exception { 224 final CpBands cpBands = segment.getCpBands(); 225 final int[] indices = decodeBandInt(name, in, codec, count); 226 final CPInterfaceMethodRef[] result = new CPInterfaceMethodRef[indices.length]; 227 for (int i1 = 0; i1 < count; i1++) { 228 result[i1] = cpBands.cpIMethodValue(indices[i1]); 229 } 230 return result; 231 } 232 233 public CPInteger[] parseCPIntReferences(final String name, final InputStream in, final BHSDCodec codec, 234 final int count) throws IOException, Pack200Exception { 235 final int[] reference = segment.getCpBands().getCpInt(); 236 final int[] indices = decodeBandInt(name, in, codec, count); 237 final CPInteger[] result = new CPInteger[indices.length]; 238 for (int i1 = 0; i1 < count; i1++) { 239 final int index = indices[i1]; 240 if (index < 0 || index >= reference.length) { 241 throw new Pack200Exception("Something has gone wrong during parsing references, index = " + index 242 + ", array size = " + reference.length); 243 } 244 result[i1] = segment.getCpBands().cpIntegerValue(index); 245 } 246 return result; 247 } 248 249 public CPLong[] parseCPLongReferences(final String name, final InputStream in, final BHSDCodec codec, 250 final int count) throws IOException, Pack200Exception { 251 final long[] reference = segment.getCpBands().getCpLong(); 252 final int[] indices = decodeBandInt(name, in, codec, count); 253 final CPLong[] result = new CPLong[indices.length]; 254 for (int i1 = 0; i1 < count; i1++) { 255 final int index = indices[i1]; 256 if (index < 0 || index >= reference.length) { 257 throw new Pack200Exception("Something has gone wrong during parsing references, index = " + index 258 + ", array size = " + reference.length); 259 } 260 result[i1] = segment.getCpBands().cpLongValue(index); 261 } 262 return result; 263 } 264 265 public CPMethodRef[] parseCPMethodRefReferences(final String name, final InputStream in, final BHSDCodec codec, 266 final int count) throws IOException, Pack200Exception { 267 final CpBands cpBands = segment.getCpBands(); 268 final int[] indices = decodeBandInt(name, in, codec, count); 269 final CPMethodRef[] result = new CPMethodRef[indices.length]; 270 for (int i1 = 0; i1 < count; i1++) { 271 result[i1] = cpBands.cpMethodValue(indices[i1]); 272 } 273 return result; 274 } 275 276 public CPUTF8[] parseCPSignatureReferences(final String name, final InputStream in, final BHSDCodec codec, 277 final int count) throws IOException, Pack200Exception { 278 final int[] indices = decodeBandInt(name, in, codec, count); 279 final CPUTF8[] result = new CPUTF8[indices.length]; 280 for (int i1 = 0; i1 < count; i1++) { 281 result[i1] = segment.getCpBands().cpSignatureValue(indices[i1]); 282 } 283 return result; 284 } 285 286 protected CPUTF8[][] parseCPSignatureReferences(final String name, final InputStream in, final BHSDCodec codec, 287 final int[] counts) throws IOException, Pack200Exception { 288 final CPUTF8[][] result = new CPUTF8[counts.length][]; 289 int sum = 0; 290 for (int i = 0; i < counts.length; i++) { 291 result[i] = new CPUTF8[counts[i]]; 292 sum += counts[i]; 293 } 294 final CPUTF8[] result1 = new CPUTF8[sum]; 295 final int[] indices = decodeBandInt(name, in, codec, sum); 296 for (int i1 = 0; i1 < sum; i1++) { 297 result1[i1] = segment.getCpBands().cpSignatureValue(indices[i1]); 298 } 299 int pos = 0; 300 for (int i = 0; i < counts.length; i++) { 301 final int num = counts[i]; 302 result[i] = new CPUTF8[num]; 303 System.arraycopy(result1, pos, result[i], 0, num); 304 pos += num; 305 } 306 return result; 307 } 308 309 public CPString[] parseCPStringReferences(final String name, final InputStream in, final BHSDCodec codec, 310 final int count) throws IOException, Pack200Exception { 311 final int[] indices = decodeBandInt(name, in, codec, count); 312 final CPString[] result = new CPString[indices.length]; 313 for (int i1 = 0; i1 < count; i1++) { 314 result[i1] = segment.getCpBands().cpStringValue(indices[i1]); 315 } 316 return result; 317 } 318 319 public CPUTF8[] parseCPUTF8References(final String name, final InputStream in, final BHSDCodec codec, 320 final int count) throws IOException, Pack200Exception { 321 final int[] indices = decodeBandInt(name, in, codec, count); 322 final CPUTF8[] result = new CPUTF8[indices.length]; 323 for (int i1 = 0; i1 < count; i1++) { 324 final int index = indices[i1]; 325 result[i1] = segment.getCpBands().cpUTF8Value(index); 326 } 327 return result; 328 } 329 330 public CPUTF8[][] parseCPUTF8References(final String name, final InputStream in, final BHSDCodec codec, 331 final int[] counts) throws IOException, Pack200Exception { 332 final CPUTF8[][] result = new CPUTF8[counts.length][]; 333 int sum = 0; 334 for (int i = 0; i < counts.length; i++) { 335 result[i] = new CPUTF8[counts[i]]; 336 sum += counts[i]; 337 } 338 final CPUTF8[] result1 = new CPUTF8[sum]; 339 final int[] indices = decodeBandInt(name, in, codec, sum); 340 for (int i1 = 0; i1 < sum; i1++) { 341 final int index = indices[i1]; 342 result1[i1] = segment.getCpBands().cpUTF8Value(index); 343 } 344 int pos = 0; 345 for (int i = 0; i < counts.length; i++) { 346 final int num = counts[i]; 347 result[i] = new CPUTF8[num]; 348 System.arraycopy(result1, pos, result[i], 0, num); 349 pos += num; 350 } 351 return result; 352 } 353 354 public long[] parseFlags(final String name, final InputStream in, final int count, final BHSDCodec hiCodec, 355 final BHSDCodec loCodec) throws IOException, Pack200Exception { 356 return parseFlags(name, in, new int[] {count}, hiCodec, loCodec)[0]; 357 } 358 359 public long[] parseFlags(final String name, final InputStream in, final int count, final BHSDCodec codec, 360 final boolean hasHi) throws IOException, Pack200Exception { 361 return parseFlags(name, in, new int[] {count}, hasHi ? codec : null, codec)[0]; 362 } 363 364 public long[][] parseFlags(final String name, final InputStream in, final int[] counts, final BHSDCodec hiCodec, 365 final BHSDCodec loCodec) throws IOException, Pack200Exception { 366 final int count = counts.length; 367 if (count == 0) { 368 return new long[][] {{}}; 369 } 370 int sum = 0; 371 final long[][] result = new long[count][]; 372 for (int i = 0; i < count; i++) { 373 result[i] = new long[counts[i]]; 374 sum += counts[i]; 375 } 376 int[] hi = null; 377 int[] lo; 378 if (hiCodec != null) { 379 hi = decodeBandInt(name, in, hiCodec, sum); 380 } 381 lo = decodeBandInt(name, in, loCodec, sum); 382 383 int index = 0; 384 for (int i = 0; i < result.length; i++) { 385 for (int j = 0; j < result[i].length; j++) { 386 if (hi != null) { 387 result[i][j] = (long) hi[index] << 32 | lo[index] & 4294967295L; 388 } else { 389 result[i][j] = lo[index]; 390 } 391 index++; 392 } 393 } 394 return result; 395 } 396 397 public long[][] parseFlags(final String name, final InputStream in, final int[] counts, final BHSDCodec codec, 398 final boolean hasHi) throws IOException, Pack200Exception { 399 return parseFlags(name, in, counts, hasHi ? codec : null, codec); 400 } 401 402 /** 403 * Parses <i>count</i> references from {@code in}, using {@code codec} to decode the values as indexes 404 * into {@code reference} (which is populated prior to this call). An exception is thrown if a decoded index 405 * falls outside the range [0..reference.length-1]. 406 * 407 * @param name the band name 408 * @param in the input stream to read from 409 * @param codec the BHSDCodec to use for decoding 410 * @param count the number of references to decode 411 * @param reference the array of values to use for the references 412 * @return Parsed references. 413 * 414 * @throws IOException if a problem occurs during reading from the underlying stream 415 * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported Codec 416 */ 417 public String[] parseReferences(final String name, final InputStream in, final BHSDCodec codec, final int count, 418 final String[] reference) throws IOException, Pack200Exception { 419 return parseReferences(name, in, codec, new int[] {count}, reference)[0]; 420 } 421 422 /** 423 * Parses <i>count</i> references from {@code in}, using {@code codec} to decode the values as indexes 424 * into {@code reference} (which is populated prior to this call). An exception is thrown if a decoded index 425 * falls outside the range [0..reference.length-1]. Unlike the other parseReferences, this post-processes the result 426 * into an array of results. 427 * 428 * @param name TODO 429 * @param in the input stream to read from 430 * @param codec the BHSDCodec to use for decoding 431 * @param counts the numbers of references to decode for each array entry 432 * @param reference the array of values to use for the references 433 * @return Parsed references. 434 * 435 * @throws IOException if a problem occurs during reading from the underlying stream 436 * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported Codec 437 */ 438 public String[][] parseReferences(final String name, final InputStream in, final BHSDCodec codec, 439 final int[] counts, final String[] reference) throws IOException, Pack200Exception { 440 final int count = counts.length; 441 if (count == 0) { 442 return new String[][] {{}}; 443 } 444 final String[][] result = new String[count][]; 445 int sum = 0; 446 for (int i = 0; i < count; i++) { 447 result[i] = new String[counts[i]]; 448 sum += counts[i]; 449 } 450 // TODO Merge the decode and parsing of a multiple structure into one 451 final String[] result1 = new String[sum]; 452 final int[] indices = decodeBandInt(name, in, codec, sum); 453 for (int i1 = 0; i1 < sum; i1++) { 454 final int index = indices[i1]; 455 if (index < 0 || index >= reference.length) { 456 throw new Pack200Exception("Something has gone wrong during parsing references, index = " + index 457 + ", array size = " + reference.length); 458 } 459 result1[i1] = reference[index]; 460 } 461 // TODO Merge the decode and parsing of a multiple structure into one 462 int pos = 0; 463 for (int i = 0; i < count; i++) { 464 final int num = counts[i]; 465 result[i] = new String[num]; 466 System.arraycopy(result1, pos, result[i], 0, num); 467 pos += num; 468 } 469 return result; 470 } 471 472 public abstract void read(InputStream inputStream) throws IOException, Pack200Exception; 473 474 public abstract void unpack() throws IOException, Pack200Exception; 475 476 public void unpack(final InputStream in) throws IOException, Pack200Exception { 477 read(in); 478 unpack(); 479 } 480 481}