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