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