BandSet.java

  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. import java.io.IOException;
  19. import java.io.InputStream;
  20. import java.util.Arrays;

  21. import org.apache.commons.compress.harmony.pack200.BHSDCodec;
  22. import org.apache.commons.compress.harmony.pack200.Codec;
  23. import org.apache.commons.compress.harmony.pack200.CodecEncoding;
  24. import org.apache.commons.compress.harmony.pack200.Pack200Exception;
  25. import org.apache.commons.compress.harmony.pack200.PopulationCodec;
  26. import org.apache.commons.compress.harmony.unpack200.bytecode.CPClass;
  27. import org.apache.commons.compress.harmony.unpack200.bytecode.CPDouble;
  28. import org.apache.commons.compress.harmony.unpack200.bytecode.CPFieldRef;
  29. import org.apache.commons.compress.harmony.unpack200.bytecode.CPFloat;
  30. import org.apache.commons.compress.harmony.unpack200.bytecode.CPInteger;
  31. import org.apache.commons.compress.harmony.unpack200.bytecode.CPInterfaceMethodRef;
  32. import org.apache.commons.compress.harmony.unpack200.bytecode.CPLong;
  33. import org.apache.commons.compress.harmony.unpack200.bytecode.CPMethodRef;
  34. import org.apache.commons.compress.harmony.unpack200.bytecode.CPNameAndType;
  35. import org.apache.commons.compress.harmony.unpack200.bytecode.CPString;
  36. import org.apache.commons.compress.harmony.unpack200.bytecode.CPUTF8;
  37. import org.apache.commons.compress.utils.ExactMath;

  38. /**
  39.  * Abstract superclass for a set of bands.
  40.  */
  41. public abstract class BandSet {

  42.     protected Segment segment;

  43.     protected SegmentHeader header;

  44.     public BandSet(final Segment segment) {
  45.         this.segment = segment;
  46.         this.header = segment.getSegmentHeader();
  47.     }

  48.     /**
  49.      * Decodes a band and return an array of {@code int} values.
  50.      *
  51.      * @param name  the name of the band (primarily for logging/debugging purposes)
  52.      * @param in    the InputStream to decode from
  53.      * @param codec the default Codec for this band
  54.      * @param count the number of elements to read
  55.      * @return an array of decoded {@code int} values
  56.      * @throws IOException      if there is a problem reading from the underlying input stream
  57.      * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid
  58.      */
  59.     public int[] decodeBandInt(final String name, final InputStream in, final BHSDCodec codec, final int count) throws IOException, Pack200Exception {
  60.         if (count < 0) {
  61.             throw new Pack200Exception("count < 0");
  62.         }
  63.         // Useful for debugging
  64. //        if (count > 0) {
  65. //            System.out.println("decoding " + name + " " + count);
  66. //        }
  67.         Codec codecUsed = codec;
  68.         if (codec.getB() == 1 || count == 0) {
  69.             return codec.decodeInts(count, in);
  70.         }
  71.         final int[] getFirst = codec.decodeInts(1, in);
  72.         if (getFirst.length == 0) {
  73.             return getFirst;
  74.         }
  75.         final int first = getFirst[0];
  76.         int[] band;
  77.         if (codec.isSigned() && first >= -256 && first <= -1) {
  78.             // Non-default codec should be used
  79.             codecUsed = CodecEncoding.getCodec(-1 - first, header.getBandHeadersInputStream(), codec);
  80.             band = codecUsed.decodeInts(count, in);
  81.         } else if (!codec.isSigned() && first >= codec.getL() && first <= codec.getL() + 255) {
  82.             // Non-default codec should be used
  83.             codecUsed = CodecEncoding.getCodec(first - codec.getL(), header.getBandHeadersInputStream(), codec);
  84.             band = codecUsed.decodeInts(count, in);
  85.         } else {
  86.             // First element should not be discarded
  87.             band = codec.decodeInts(count - 1, in, first);
  88.         }
  89.         // Useful for debugging -E options:
  90.         // if (!codecUsed.equals(codec)) {
  91.         // int bytes = codecUsed.lastBandLength;
  92.         // System.out.println(count + " " + name + " encoded with " + codecUsed + " " + bytes);
  93.         // }
  94.         if (codecUsed instanceof PopulationCodec) {
  95.             final PopulationCodec popCodec = (PopulationCodec) codecUsed;
  96.             final int[] favoured = popCodec.getFavoured().clone();
  97.             Arrays.sort(favoured);
  98.             for (int i = 0; i < band.length; i++) {
  99.                 final boolean favouredValue = Arrays.binarySearch(favoured, band[i]) > -1;
  100.                 final Codec theCodec = favouredValue ? popCodec.getFavouredCodec() : popCodec.getUnfavouredCodec();
  101.                 if (theCodec instanceof BHSDCodec && ((BHSDCodec) theCodec).isDelta()) {
  102.                     final BHSDCodec bhsd = (BHSDCodec) theCodec;
  103.                     final long cardinality = bhsd.cardinality();
  104.                     while (band[i] > bhsd.largest()) {
  105.                         band[i] -= cardinality;
  106.                     }
  107.                     while (band[i] < bhsd.smallest()) {
  108.                         band[i] = ExactMath.add(band[i], cardinality);
  109.                     }
  110.                 }
  111.             }
  112.         }
  113.         return band;
  114.     }

  115.     /**
  116.      * Decodes a band and return an array of {@code int[]} values.
  117.      *
  118.      * @param name         the name of the band (primarily for logging/debugging purposes)
  119.      * @param in           the InputStream to decode from
  120.      * @param defaultCodec the default codec for this band
  121.      * @param counts       the numbers of elements to read for each int array within the array to be returned
  122.      * @return an array of decoded {@code int[]} values
  123.      * @throws IOException      if there is a problem reading from the underlying input stream
  124.      * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid
  125.      */
  126.     public int[][] decodeBandInt(final String name, final InputStream in, final BHSDCodec defaultCodec, final int[] counts)
  127.             throws IOException, Pack200Exception {
  128.         final int[][] result = new int[counts.length][];
  129.         int totalCount = 0;
  130.         for (final int count : counts) {
  131.             totalCount += count;
  132.         }
  133.         final int[] twoDResult = decodeBandInt(name, in, defaultCodec, totalCount);
  134.         int index = 0;
  135.         for (int i = 0; i < result.length; i++) {
  136.             if (counts[i] > twoDResult.length) {
  137.                 throw new IOException("Counts value exceeds length of twoDResult");
  138.             }
  139.             result[i] = new int[counts[i]];
  140.             for (int j = 0; j < result[i].length; j++) {
  141.                 result[i][j] = twoDResult[index];
  142.                 index++;
  143.             }
  144.         }
  145.         return result;
  146.     }

  147.     protected String[] getReferences(final int[] ints, final String[] reference) {
  148.         final String[] result = new String[ints.length];
  149.         Arrays.setAll(result, i -> reference[ints[i]]);
  150.         return result;
  151.     }

  152.     protected String[][] getReferences(final int[][] ints, final String[] reference) {
  153.         final String[][] result = new String[ints.length][];
  154.         for (int i = 0; i < result.length; i++) {
  155.             result[i] = new String[ints[i].length];
  156.             for (int j = 0; j < result[i].length; j++) {
  157.                 result[i][j] = reference[ints[i][j]];
  158.             }
  159.         }
  160.         return result;
  161.     }

  162.     public CPClass[] parseCPClassReferences(final String name, final InputStream in, final BHSDCodec codec, final int count)
  163.             throws IOException, Pack200Exception {
  164.         final int[] indices = decodeBandInt(name, in, codec, count);
  165.         final CPClass[] result = new CPClass[indices.length];
  166.         for (int i1 = 0; i1 < count; i1++) {
  167.             result[i1] = segment.getCpBands().cpClassValue(indices[i1]);
  168.         }
  169.         return result;
  170.     }

  171.     public CPNameAndType[] parseCPDescriptorReferences(final String name, final InputStream in, final BHSDCodec codec, final int count)
  172.             throws IOException, Pack200Exception {
  173.         final CpBands cpBands = segment.getCpBands();
  174.         final int[] indices = decodeBandInt(name, in, codec, count);
  175.         final CPNameAndType[] result = new CPNameAndType[indices.length];
  176.         for (int i1 = 0; i1 < count; i1++) {
  177.             final int index = indices[i1];
  178.             result[i1] = cpBands.cpNameAndTypeValue(index);
  179.         }
  180.         return result;
  181.     }

  182.     public CPDouble[] parseCPDoubleReferences(final String name, final InputStream in, final BHSDCodec codec, final int count)
  183.             throws IOException, Pack200Exception {
  184.         final int[] indices = decodeBandInt(name, in, codec, count);
  185.         final CPDouble[] result = new CPDouble[indices.length];
  186.         for (int i1 = 0; i1 < count; i1++) {
  187.             result[i1] = segment.getCpBands().cpDoubleValue(indices[i1]);
  188.         }
  189.         return result;
  190.     }

  191.     public CPFieldRef[] parseCPFieldRefReferences(final String name, final InputStream in, final BHSDCodec codec, final int count)
  192.             throws IOException, Pack200Exception {
  193.         final CpBands cpBands = segment.getCpBands();
  194.         final int[] indices = decodeBandInt(name, in, codec, count);
  195.         final CPFieldRef[] result = new CPFieldRef[indices.length];
  196.         for (int i1 = 0; i1 < count; i1++) {
  197.             final int index = indices[i1];
  198.             result[i1] = cpBands.cpFieldValue(index);
  199.         }
  200.         return result;
  201.     }

  202.     public CPFloat[] parseCPFloatReferences(final String name, final InputStream in, final BHSDCodec codec, final int count)
  203.             throws IOException, Pack200Exception {
  204.         final int[] indices = decodeBandInt(name, in, codec, count);
  205.         final CPFloat[] result = new CPFloat[indices.length];
  206.         for (int i1 = 0; i1 < count; i1++) {
  207.             result[i1] = segment.getCpBands().cpFloatValue(indices[i1]);
  208.         }
  209.         return result;
  210.     }

  211.     public CPInterfaceMethodRef[] parseCPInterfaceMethodRefReferences(final String name, final InputStream in, final BHSDCodec codec, final int count)
  212.             throws IOException, Pack200Exception {
  213.         final CpBands cpBands = segment.getCpBands();
  214.         final int[] indices = decodeBandInt(name, in, codec, count);
  215.         final CPInterfaceMethodRef[] result = new CPInterfaceMethodRef[indices.length];
  216.         for (int i1 = 0; i1 < count; i1++) {
  217.             result[i1] = cpBands.cpIMethodValue(indices[i1]);
  218.         }
  219.         return result;
  220.     }

  221.     public CPInteger[] parseCPIntReferences(final String name, final InputStream in, final BHSDCodec codec, final int count)
  222.             throws IOException, Pack200Exception {
  223.         final int[] reference = segment.getCpBands().getCpInt();
  224.         final int[] indices = decodeBandInt(name, in, codec, count);
  225.         final CPInteger[] result = new CPInteger[indices.length];
  226.         for (int i1 = 0; i1 < count; i1++) {
  227.             final int index = indices[i1];
  228.             if (index < 0 || index >= reference.length) {
  229.                 throw new Pack200Exception("Something has gone wrong during parsing references, index = " + index + ", array size = " + reference.length);
  230.             }
  231.             result[i1] = segment.getCpBands().cpIntegerValue(index);
  232.         }
  233.         return result;
  234.     }

  235.     public CPLong[] parseCPLongReferences(final String name, final InputStream in, final BHSDCodec codec, final int count)
  236.             throws IOException, Pack200Exception {
  237.         final long[] reference = segment.getCpBands().getCpLong();
  238.         final int[] indices = decodeBandInt(name, in, codec, count);
  239.         final CPLong[] result = new CPLong[indices.length];
  240.         for (int i1 = 0; i1 < count; i1++) {
  241.             final int index = indices[i1];
  242.             if (index < 0 || index >= reference.length) {
  243.                 throw new Pack200Exception("Something has gone wrong during parsing references, index = " + index + ", array size = " + reference.length);
  244.             }
  245.             result[i1] = segment.getCpBands().cpLongValue(index);
  246.         }
  247.         return result;
  248.     }

  249.     public CPMethodRef[] parseCPMethodRefReferences(final String name, final InputStream in, final BHSDCodec codec, final int count)
  250.             throws IOException, Pack200Exception {
  251.         final CpBands cpBands = segment.getCpBands();
  252.         final int[] indices = decodeBandInt(name, in, codec, count);
  253.         final CPMethodRef[] result = new CPMethodRef[indices.length];
  254.         for (int i1 = 0; i1 < count; i1++) {
  255.             result[i1] = cpBands.cpMethodValue(indices[i1]);
  256.         }
  257.         return result;
  258.     }

  259.     public CPUTF8[] parseCPSignatureReferences(final String name, final InputStream in, final BHSDCodec codec, final int count)
  260.             throws IOException, Pack200Exception {
  261.         final int[] indices = decodeBandInt(name, in, codec, count);
  262.         final CPUTF8[] result = new CPUTF8[indices.length];
  263.         for (int i1 = 0; i1 < count; i1++) {
  264.             result[i1] = segment.getCpBands().cpSignatureValue(indices[i1]);
  265.         }
  266.         return result;
  267.     }

  268.     protected CPUTF8[][] parseCPSignatureReferences(final String name, final InputStream in, final BHSDCodec codec, final int[] counts)
  269.             throws IOException, Pack200Exception {
  270.         int sum = 0;
  271.         for (int i = 0; i < counts.length; i++) {
  272.             sum += counts[i];
  273.         }
  274.         final int[] indices = decodeBandInt(name, in, codec, sum);
  275.         final CPUTF8[] result1 = new CPUTF8[sum];
  276.         for (int i1 = 0; i1 < sum; i1++) {
  277.             result1[i1] = segment.getCpBands().cpSignatureValue(indices[i1]);
  278.         }
  279.         int pos = 0;
  280.         final CPUTF8[][] result = new CPUTF8[counts.length][];
  281.         for (int i = 0; i < counts.length; i++) {
  282.             final int num = counts[i];
  283.             result[i] = new CPUTF8[num];
  284.             System.arraycopy(result1, pos, result[i], 0, num);
  285.             pos += num;
  286.         }
  287.         return result;
  288.     }

  289.     public CPString[] parseCPStringReferences(final String name, final InputStream in, final BHSDCodec codec, final int count)
  290.             throws IOException, Pack200Exception {
  291.         final int[] indices = decodeBandInt(name, in, codec, count);
  292.         final CPString[] result = new CPString[indices.length];
  293.         for (int i1 = 0; i1 < count; i1++) {
  294.             result[i1] = segment.getCpBands().cpStringValue(indices[i1]);
  295.         }
  296.         return result;
  297.     }

  298.     public CPUTF8[] parseCPUTF8References(final String name, final InputStream in, final BHSDCodec codec, final int count)
  299.             throws IOException, Pack200Exception {
  300.         final int[] indices = decodeBandInt(name, in, codec, count);
  301.         final CPUTF8[] result = new CPUTF8[indices.length];
  302.         for (int i1 = 0; i1 < count; i1++) {
  303.             final int index = indices[i1];
  304.             result[i1] = segment.getCpBands().cpUTF8Value(index);
  305.         }
  306.         return result;
  307.     }

  308.     public CPUTF8[][] parseCPUTF8References(final String name, final InputStream in, final BHSDCodec codec, final int[] counts)
  309.             throws IOException, Pack200Exception {
  310.         final CPUTF8[][] result = new CPUTF8[counts.length][];
  311.         int sum = 0;
  312.         for (int i = 0; i < counts.length; i++) {
  313.             result[i] = new CPUTF8[counts[i]];
  314.             sum += counts[i];
  315.         }
  316.         final CPUTF8[] result1 = new CPUTF8[sum];
  317.         final int[] indices = decodeBandInt(name, in, codec, sum);
  318.         for (int i1 = 0; i1 < sum; i1++) {
  319.             final int index = indices[i1];
  320.             result1[i1] = segment.getCpBands().cpUTF8Value(index);
  321.         }
  322.         int pos = 0;
  323.         for (int i = 0; i < counts.length; i++) {
  324.             final int num = counts[i];
  325.             result[i] = new CPUTF8[num];
  326.             System.arraycopy(result1, pos, result[i], 0, num);
  327.             pos += num;
  328.         }
  329.         return result;
  330.     }

  331.     public long[] parseFlags(final String name, final InputStream in, final int count, final BHSDCodec hiCodec, final BHSDCodec loCodec)
  332.             throws IOException, Pack200Exception {
  333.         return parseFlags(name, in, new int[] { count }, hiCodec, loCodec)[0];
  334.     }

  335.     public long[] parseFlags(final String name, final InputStream in, final int count, final BHSDCodec codec, final boolean hasHi)
  336.             throws IOException, Pack200Exception {
  337.         return parseFlags(name, in, new int[] { count }, hasHi ? codec : null, codec)[0];
  338.     }

  339.     public long[][] parseFlags(final String name, final InputStream in, final int[] counts, final BHSDCodec hiCodec, final BHSDCodec loCodec)
  340.             throws IOException, Pack200Exception {
  341.         final int count = counts.length;
  342.         if (count == 0) {
  343.             return new long[][] { {} };
  344.         }
  345.         int sum = 0;
  346.         final long[][] result = new long[count][];
  347.         for (int i = 0; i < count; i++) {
  348.             sum += counts[i];
  349.         }
  350.         int[] hi = null;
  351.         int[] lo;
  352.         if (hiCodec != null) {
  353.             hi = decodeBandInt(name, in, hiCodec, sum);
  354.         }
  355.         lo = decodeBandInt(name, in, loCodec, sum);

  356.         int index = 0;
  357.         for (int i = 0; i < count; i++) {
  358.             result[i] = new long[counts[i]];
  359.             for (int j = 0; j < result[i].length; j++) {
  360.                 if (hi != null) {
  361.                     result[i][j] = (long) hi[index] << 32 | lo[index] & 4294967295L;
  362.                 } else {
  363.                     result[i][j] = lo[index];
  364.                 }
  365.                 index++;
  366.             }
  367.         }
  368.         return result;
  369.     }

  370.     public long[][] parseFlags(final String name, final InputStream in, final int[] counts, final BHSDCodec codec, final boolean hasHi)
  371.             throws IOException, Pack200Exception {
  372.         return parseFlags(name, in, counts, hasHi ? codec : null, codec);
  373.     }

  374.     /**
  375.      * 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
  376.      * this call). An exception is thrown if a decoded index falls outside the range [0..reference.length-1].
  377.      *
  378.      * @param name      the band name
  379.      * @param in        the input stream to read from
  380.      * @param codec     the BHSDCodec to use for decoding
  381.      * @param count     the number of references to decode
  382.      * @param reference the array of values to use for the references
  383.      * @return Parsed references.
  384.      *
  385.      * @throws IOException      if a problem occurs during reading from the underlying stream
  386.      * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported Codec
  387.      */
  388.     public String[] parseReferences(final String name, final InputStream in, final BHSDCodec codec, final int count, final String[] reference)
  389.             throws IOException, Pack200Exception {
  390.         return parseReferences(name, in, codec, new int[] { count }, reference)[0];
  391.     }

  392.     /**
  393.      * 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
  394.      * this call). An exception is thrown if a decoded index falls outside the range [0..reference.length-1]. Unlike the other parseReferences, this
  395.      * post-processes the result into an array of results.
  396.      *
  397.      * @param name      TODO
  398.      * @param in        the input stream to read from
  399.      * @param codec     the BHSDCodec to use for decoding
  400.      * @param counts    the numbers of references to decode for each array entry
  401.      * @param reference the array of values to use for the references
  402.      * @return Parsed references.
  403.      *
  404.      * @throws IOException      if a problem occurs during reading from the underlying stream
  405.      * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported Codec
  406.      */
  407.     public String[][] parseReferences(final String name, final InputStream in, final BHSDCodec codec, final int[] counts, final String[] reference)
  408.             throws IOException, Pack200Exception {
  409.         final int count = counts.length;
  410.         if (count == 0) {
  411.             return new String[][] { {} };
  412.         }
  413.         int sum = 0;
  414.         for (int i = 0; i < count; i++) {
  415.             sum += counts[i];
  416.         }
  417.         // TODO Merge the decode and parsing of a multiple structure into one
  418.         final int[] indices = decodeBandInt(name, in, codec, sum);
  419.         final String[] result1 = new String[sum];
  420.         for (int i1 = 0; i1 < sum; i1++) {
  421.             final int index = indices[i1];
  422.             if (index < 0 || index >= reference.length) {
  423.                 throw new Pack200Exception("Something has gone wrong during parsing references, index = " + index + ", array size = " + reference.length);
  424.             }
  425.             result1[i1] = reference[index];
  426.         }
  427.         // TODO Merge the decode and parsing of a multiple structure into one
  428.         final String[][] result = new String[count][];
  429.         int pos = 0;
  430.         for (int i = 0; i < count; i++) {
  431.             final int num = counts[i];
  432.             result[i] = new String[num];
  433.             System.arraycopy(result1, pos, result[i], 0, num);
  434.             pos += num;
  435.         }
  436.         return result;
  437.     }

  438.     public abstract void read(InputStream inputStream) throws IOException, Pack200Exception;

  439.     public abstract void unpack() throws IOException, Pack200Exception;

  440.     public void unpack(final InputStream in) throws IOException, Pack200Exception {
  441.         read(in);
  442.         unpack();
  443.     }

  444. }