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