View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one or more
3    *  contributor license agreements.  See the NOTICE file distributed with
4    *  this work for additional information regarding copyright ownership.
5    *  The ASF licenses this file to You under the Apache License, Version 2.0
6    *  (the "License"); you may not use this file except in compliance with
7    *  the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  package org.apache.commons.compress.harmony.unpack200;
18  
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.util.Arrays;
22  
23  import org.apache.commons.compress.harmony.pack200.BHSDCodec;
24  import org.apache.commons.compress.harmony.pack200.Codec;
25  import org.apache.commons.compress.harmony.pack200.CodecEncoding;
26  import org.apache.commons.compress.harmony.pack200.Pack200Exception;
27  import org.apache.commons.compress.harmony.pack200.PopulationCodec;
28  import org.apache.commons.compress.harmony.unpack200.bytecode.CPClass;
29  import org.apache.commons.compress.harmony.unpack200.bytecode.CPDouble;
30  import org.apache.commons.compress.harmony.unpack200.bytecode.CPFieldRef;
31  import org.apache.commons.compress.harmony.unpack200.bytecode.CPFloat;
32  import org.apache.commons.compress.harmony.unpack200.bytecode.CPInteger;
33  import org.apache.commons.compress.harmony.unpack200.bytecode.CPInterfaceMethodRef;
34  import org.apache.commons.compress.harmony.unpack200.bytecode.CPLong;
35  import org.apache.commons.compress.harmony.unpack200.bytecode.CPMethodRef;
36  import org.apache.commons.compress.harmony.unpack200.bytecode.CPNameAndType;
37  import org.apache.commons.compress.harmony.unpack200.bytecode.CPString;
38  import org.apache.commons.compress.harmony.unpack200.bytecode.CPUTF8;
39  import org.apache.commons.compress.utils.ExactMath;
40  
41  /**
42   * Abstract superclass for a set of bands.
43   */
44  public abstract class BandSet {
45  
46      protected Segment segment;
47  
48      protected SegmentHeader header;
49  
50      public BandSet(final Segment segment) {
51          this.segment = segment;
52          this.header = segment.getSegmentHeader();
53      }
54  
55      /**
56       * Decodes a band and return an array of {@code int} values.
57       *
58       * @param name  the name of the band (primarily for logging/debugging purposes)
59       * @param in    the InputStream to decode from
60       * @param codec the default Codec for this band
61       * @param count the number of elements to read
62       * @return an array of decoded {@code int} values
63       * @throws IOException      if there is a problem reading from the underlying input stream
64       * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid
65       */
66      public int[] decodeBandInt(final String name, final InputStream in, final BHSDCodec codec, final int count) throws IOException, Pack200Exception {
67          if (count < 0) {
68              throw new Pack200Exception("count < 0");
69          }
70          // Useful for debugging
71  //        if (count > 0) {
72  //            System.out.println("decoding " + name + " " + count);
73  //        }
74          Codec codecUsed = codec;
75          if (codec.getB() == 1 || count == 0) {
76              return codec.decodeInts(count, in);
77          }
78          final int[] getFirst = codec.decodeInts(1, in);
79          if (getFirst.length == 0) {
80              return getFirst;
81          }
82          final int first = getFirst[0];
83          int[] band;
84          if (codec.isSigned() && first >= -256 && first <= -1) {
85              // Non-default codec should be used
86              codecUsed = CodecEncoding.getCodec(-1 - first, header.getBandHeadersInputStream(), codec);
87              band = codecUsed.decodeInts(count, in);
88          } else if (!codec.isSigned() && first >= codec.getL() && first <= codec.getL() + 255) {
89              // Non-default codec should be used
90              codecUsed = CodecEncoding.getCodec(first - codec.getL(), header.getBandHeadersInputStream(), codec);
91              band = codecUsed.decodeInts(count, in);
92          } else {
93              // First element should not be discarded
94              band = codec.decodeInts(count - 1, in, first);
95          }
96          // Useful for debugging -E options:
97          // if (!codecUsed.equals(codec)) {
98          // int bytes = codecUsed.lastBandLength;
99          // 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 <i>count</i> 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 <i>count</i> 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 }