CodecEncoding.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.pack200;

  18. import java.io.EOFException;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.util.Arrays;
  22. import java.util.HashMap;
  23. import java.util.Map;

  24. /**
  25.  * CodecEncoding is used to get the right Codec for a given meta-encoding.
  26.  */
  27. public class CodecEncoding {

  28.     private static final int[] EMPTY_INT_ARRAY = {};

  29.     /**
  30.      * The canonical encodings are defined to allow a single byte to represent one of the standard encodings. The following values are defined in the Pack200
  31.      * specification, and this array cannot be changed.
  32.      */
  33.     private static final BHSDCodec[] canonicalCodec = { null, new BHSDCodec(1, 256), new BHSDCodec(1, 256, 1), new BHSDCodec(1, 256, 0, 1),
  34.             new BHSDCodec(1, 256, 1, 1), new BHSDCodec(2, 256), new BHSDCodec(2, 256, 1), new BHSDCodec(2, 256, 0, 1), new BHSDCodec(2, 256, 1, 1),
  35.             new BHSDCodec(3, 256), new BHSDCodec(3, 256, 1), new BHSDCodec(3, 256, 0, 1), new BHSDCodec(3, 256, 1, 1), new BHSDCodec(4, 256),
  36.             new BHSDCodec(4, 256, 1), new BHSDCodec(4, 256, 0, 1), new BHSDCodec(4, 256, 1, 1), new BHSDCodec(5, 4), new BHSDCodec(5, 4, 1),
  37.             new BHSDCodec(5, 4, 2), new BHSDCodec(5, 16), new BHSDCodec(5, 16, 1), new BHSDCodec(5, 16, 2), new BHSDCodec(5, 32), new BHSDCodec(5, 32, 1),
  38.             new BHSDCodec(5, 32, 2), new BHSDCodec(5, 64), new BHSDCodec(5, 64, 1), new BHSDCodec(5, 64, 2), new BHSDCodec(5, 128), new BHSDCodec(5, 128, 1),
  39.             new BHSDCodec(5, 128, 2), new BHSDCodec(5, 4, 0, 1), new BHSDCodec(5, 4, 1, 1), new BHSDCodec(5, 4, 2, 1), new BHSDCodec(5, 16, 0, 1),
  40.             new BHSDCodec(5, 16, 1, 1), new BHSDCodec(5, 16, 2, 1), new BHSDCodec(5, 32, 0, 1), new BHSDCodec(5, 32, 1, 1), new BHSDCodec(5, 32, 2, 1),
  41.             new BHSDCodec(5, 64, 0, 1), new BHSDCodec(5, 64, 1, 1), new BHSDCodec(5, 64, 2, 1), new BHSDCodec(5, 128, 0, 1), new BHSDCodec(5, 128, 1, 1),
  42.             new BHSDCodec(5, 128, 2, 1), new BHSDCodec(2, 192), new BHSDCodec(2, 224), new BHSDCodec(2, 240), new BHSDCodec(2, 248), new BHSDCodec(2, 252),
  43.             new BHSDCodec(2, 8, 0, 1), new BHSDCodec(2, 8, 1, 1), new BHSDCodec(2, 16, 0, 1), new BHSDCodec(2, 16, 1, 1), new BHSDCodec(2, 32, 0, 1),
  44.             new BHSDCodec(2, 32, 1, 1), new BHSDCodec(2, 64, 0, 1), new BHSDCodec(2, 64, 1, 1), new BHSDCodec(2, 128, 0, 1), new BHSDCodec(2, 128, 1, 1),
  45.             new BHSDCodec(2, 192, 0, 1), new BHSDCodec(2, 192, 1, 1), new BHSDCodec(2, 224, 0, 1), new BHSDCodec(2, 224, 1, 1), new BHSDCodec(2, 240, 0, 1),
  46.             new BHSDCodec(2, 240, 1, 1), new BHSDCodec(2, 248, 0, 1), new BHSDCodec(2, 248, 1, 1), new BHSDCodec(3, 192), new BHSDCodec(3, 224),
  47.             new BHSDCodec(3, 240), new BHSDCodec(3, 248), new BHSDCodec(3, 252), new BHSDCodec(3, 8, 0, 1), new BHSDCodec(3, 8, 1, 1),
  48.             new BHSDCodec(3, 16, 0, 1), new BHSDCodec(3, 16, 1, 1), new BHSDCodec(3, 32, 0, 1), new BHSDCodec(3, 32, 1, 1), new BHSDCodec(3, 64, 0, 1),
  49.             new BHSDCodec(3, 64, 1, 1), new BHSDCodec(3, 128, 0, 1), new BHSDCodec(3, 128, 1, 1), new BHSDCodec(3, 192, 0, 1), new BHSDCodec(3, 192, 1, 1),
  50.             new BHSDCodec(3, 224, 0, 1), new BHSDCodec(3, 224, 1, 1), new BHSDCodec(3, 240, 0, 1), new BHSDCodec(3, 240, 1, 1), new BHSDCodec(3, 248, 0, 1),
  51.             new BHSDCodec(3, 248, 1, 1), new BHSDCodec(4, 192), new BHSDCodec(4, 224), new BHSDCodec(4, 240), new BHSDCodec(4, 248), new BHSDCodec(4, 252),
  52.             new BHSDCodec(4, 8, 0, 1), new BHSDCodec(4, 8, 1, 1), new BHSDCodec(4, 16, 0, 1), new BHSDCodec(4, 16, 1, 1), new BHSDCodec(4, 32, 0, 1),
  53.             new BHSDCodec(4, 32, 1, 1), new BHSDCodec(4, 64, 0, 1), new BHSDCodec(4, 64, 1, 1), new BHSDCodec(4, 128, 0, 1), new BHSDCodec(4, 128, 1, 1),
  54.             new BHSDCodec(4, 192, 0, 1), new BHSDCodec(4, 192, 1, 1), new BHSDCodec(4, 224, 0, 1), new BHSDCodec(4, 224, 1, 1), new BHSDCodec(4, 240, 0, 1),
  55.             new BHSDCodec(4, 240, 1, 1), new BHSDCodec(4, 248, 0, 1), new BHSDCodec(4, 248, 1, 1) };

  56.     private static Map<BHSDCodec, Integer> canonicalCodecsToSpecifiers;

  57.     static {
  58.         final HashMap<BHSDCodec, Integer> reverseMap = new HashMap<>(canonicalCodec.length);
  59.         for (int i = 0; i < canonicalCodec.length; i++) {
  60.             reverseMap.put(canonicalCodec[i], Integer.valueOf(i));
  61.         }
  62.         canonicalCodecsToSpecifiers = reverseMap;
  63.     }

  64.     public static BHSDCodec getCanonicalCodec(final int i) {
  65.         return canonicalCodec[i];
  66.     }

  67.     /**
  68.      * Gets the codec specified by the given value byte and optional byte header. If the value is &gt;= 116, then bytes may be consumed from the secondary
  69.      * input stream, which is taken to be the contents of the band_headers byte array. Since the values from this are consumed and not repeated, the input
  70.      * stream should be reused for subsequent encodings. This does not therefore close the input stream.
  71.      *
  72.      * @param value        the canonical encoding value
  73.      * @param in           the input stream to read additional byte headers from
  74.      * @param defaultCodec TODO
  75.      * @return the corresponding codec, or {@code null} if the default should be used
  76.      *
  77.      * @throws IOException      if there is a problem reading from the input stream (which in reality, is never, since the band_headers are likely stored in a
  78.      *                          byte array and accessed via a ByteArrayInputStream. However, an EOFException could occur if things go wrong)
  79.      * @throws Pack200Exception TODO
  80.      */
  81.     public static Codec getCodec(final int value, final InputStream in, final Codec defaultCodec) throws IOException, Pack200Exception {
  82.         // Sanity check to make sure that no-one has changed
  83.         // the canonical codecs, which would really cause havoc
  84.         if (canonicalCodec.length != 116) {
  85.             throw new Error("Canonical encodings have been incorrectly modified");
  86.         }
  87.         if (value < 0) {
  88.             throw new IllegalArgumentException("Encoding cannot be less than zero");
  89.         }
  90.         if (value == 0) {
  91.             return defaultCodec;
  92.         }
  93.         if (value <= 115) {
  94.             return canonicalCodec[value];
  95.         }
  96.         if (value == 116) {
  97.             int code = in.read();
  98.             if (code == -1) {
  99.                 throw new EOFException("End of buffer read whilst trying to decode codec");
  100.             }
  101.             final int d = code & 0x01;
  102.             final int s = code >> 1 & 0x03;
  103.             final int b = (code >> 3 & 0x07) + 1; // this might result in an invalid
  104.             // number, but it's checked in the
  105.             // Codec constructor
  106.             code = in.read();
  107.             if (code == -1) {
  108.                 throw new EOFException("End of buffer read whilst trying to decode codec");
  109.             }
  110.             final int h = code + 1;
  111.             // This handles the special cases for invalid combinations of data.
  112.             return new BHSDCodec(b, h, s, d);
  113.         }
  114.         if (value >= 117 && value <= 140) { // Run codec
  115.             final int offset = value - 117;
  116.             final int kx = offset & 3;
  117.             final boolean kbflag = (offset >> 2 & 1) == 1;
  118.             final boolean adef = (offset >> 3 & 1) == 1;
  119.             final boolean bdef = (offset >> 4 & 1) == 1;
  120.             // If both A and B use the default encoding, what's the point of
  121.             // having a run of default values followed by default values
  122.             if (adef && bdef) {
  123.                 throw new Pack200Exception("ADef and BDef should never both be true");
  124.             }
  125.             final int kb = kbflag ? in.read() : 3;
  126.             final int k = (kb + 1) * (int) Math.pow(16, kx);
  127.             Codec aCodec, bCodec;
  128.             if (adef) {
  129.                 aCodec = defaultCodec;
  130.             } else {
  131.                 aCodec = getCodec(in.read(), in, defaultCodec);
  132.             }
  133.             if (bdef) {
  134.                 bCodec = defaultCodec;
  135.             } else {
  136.                 bCodec = getCodec(in.read(), in, defaultCodec);
  137.             }
  138.             return new RunCodec(k, aCodec, bCodec);
  139.         }
  140.         if (value < 141 || value > 188) {
  141.             throw new Pack200Exception("Invalid codec encoding byte (" + value + ") found");
  142.         }
  143.         final int offset = value - 141;
  144.         final boolean fdef = (offset & 1) == 1;
  145.         final boolean udef = (offset >> 1 & 1) == 1;
  146.         final int tdefl = offset >> 2;
  147.         final boolean tdef = tdefl != 0;
  148.         // From section 6.7.3 of spec
  149.         final int[] tdefToL = { 0, 4, 8, 16, 32, 64, 128, 192, 224, 240, 248, 252 };
  150.         final int l = tdefToL[tdefl];
  151.         // NOTE: Do not re-factor this to bring out uCodec; the order in
  152.         // which
  153.         // they are read from the stream is important
  154.         if (tdef) {
  155.             final Codec fCodec = fdef ? defaultCodec : getCodec(in.read(), in, defaultCodec);
  156.             final Codec uCodec = udef ? defaultCodec : getCodec(in.read(), in, defaultCodec);
  157.             // Unfortunately, if tdef, then tCodec depends both on l and
  158.             // also on k, the
  159.             // number of items read from the fCodec. So we don't know in
  160.             // advance what
  161.             // the codec will be.
  162.             return new PopulationCodec(fCodec, l, uCodec);
  163.         }
  164.         final Codec fCodec = fdef ? defaultCodec : getCodec(in.read(), in, defaultCodec);
  165.         final Codec tCodec = getCodec(in.read(), in, defaultCodec);
  166.         final Codec uCodec = udef ? defaultCodec : getCodec(in.read(), in, defaultCodec);
  167.         return new PopulationCodec(fCodec, tCodec, uCodec);
  168.     }

  169.     public static int[] getSpecifier(final Codec codec, final Codec defaultForBand) {
  170.         if (canonicalCodecsToSpecifiers.containsKey(codec)) {
  171.             return new int[] { canonicalCodecsToSpecifiers.get(codec).intValue() };
  172.         }
  173.         if (codec instanceof BHSDCodec) {
  174.             // Cache these?
  175.             final BHSDCodec bhsdCodec = (BHSDCodec) codec;
  176.             final int[] specifiers = new int[3];
  177.             specifiers[0] = 116;
  178.             specifiers[1] = (bhsdCodec.isDelta() ? 1 : 0) + 2 * bhsdCodec.getS() + 8 * (bhsdCodec.getB() - 1);
  179.             specifiers[2] = bhsdCodec.getH() - 1;
  180.             return specifiers;
  181.         }
  182.         if (codec instanceof RunCodec) {
  183.             final RunCodec runCodec = (RunCodec) codec;
  184.             final int k = runCodec.getK();
  185.             int kb;
  186.             int kx;
  187.             if (k <= 256) {
  188.                 kb = 0;
  189.                 kx = k - 1;
  190.             } else if (k <= 4096) {
  191.                 kb = 1;
  192.                 kx = k / 16 - 1;
  193.             } else if (k <= 65536) {
  194.                 kb = 2;
  195.                 kx = k / 256 - 1;
  196.             } else {
  197.                 kb = 3;
  198.                 kx = k / 4096 - 1;
  199.             }
  200.             final Codec aCodec = runCodec.getACodec();
  201.             final Codec bCodec = runCodec.getBCodec();
  202.             int abDef = 0;
  203.             if (aCodec.equals(defaultForBand)) {
  204.                 abDef = 1;
  205.             } else if (bCodec.equals(defaultForBand)) {
  206.                 abDef = 2;
  207.             }
  208.             final int first = 117 + kb + (kx == 3 ? 0 : 4) + 8 * abDef;
  209.             final int[] aSpecifier = abDef == 1 ? EMPTY_INT_ARRAY : getSpecifier(aCodec, defaultForBand);
  210.             final int[] bSpecifier = abDef == 2 ? EMPTY_INT_ARRAY : getSpecifier(bCodec, defaultForBand);
  211.             final int[] specifier = new int[1 + (kx == 3 ? 0 : 1) + aSpecifier.length + bSpecifier.length];
  212.             specifier[0] = first;
  213.             int index = 1;
  214.             if (kx != 3) {
  215.                 specifier[1] = kx;
  216.                 index++;
  217.             }
  218.             for (final int element : aSpecifier) {
  219.                 specifier[index] = element;
  220.                 index++;
  221.             }
  222.             for (final int element : bSpecifier) {
  223.                 specifier[index] = element;
  224.                 index++;
  225.             }
  226.             return specifier;
  227.         }
  228.         if (codec instanceof PopulationCodec) {
  229.             final PopulationCodec populationCodec = (PopulationCodec) codec;
  230.             final Codec tokenCodec = populationCodec.getTokenCodec();
  231.             final Codec favouredCodec = populationCodec.getFavouredCodec();
  232.             final Codec unfavouredCodec = populationCodec.getUnfavouredCodec();
  233.             final int fDef = favouredCodec.equals(defaultForBand) ? 1 : 0;
  234.             final int uDef = unfavouredCodec.equals(defaultForBand) ? 1 : 0;
  235.             int tDefL = 0;
  236.             final int[] favoured = populationCodec.getFavoured();
  237.             if (favoured != null) {
  238.                 if (tokenCodec == Codec.BYTE1) {
  239.                     tDefL = 1;
  240.                 } else if (tokenCodec instanceof BHSDCodec) {
  241.                     final BHSDCodec tokenBHSD = (BHSDCodec) tokenCodec;
  242.                     if (tokenBHSD.getS() == 0) {
  243.                         final int[] possibleLValues = { 4, 8, 16, 32, 64, 128, 192, 224, 240, 248, 252 };
  244.                         final int l = 256 - tokenBHSD.getH();
  245.                         int index = Arrays.binarySearch(possibleLValues, l);
  246.                         if (index != -1) {
  247.                             // TODO: check range is ok for ks
  248.                             tDefL = index++;
  249.                         }
  250.                     }
  251.                 }
  252.             }
  253.             final int first = 141 + fDef + 2 * uDef + 4 * tDefL;
  254.             final int[] favouredSpecifier = fDef == 1 ? EMPTY_INT_ARRAY : getSpecifier(favouredCodec, defaultForBand);
  255.             final int[] tokenSpecifier = tDefL != 0 ? EMPTY_INT_ARRAY : getSpecifier(tokenCodec, defaultForBand);
  256.             final int[] unfavouredSpecifier = uDef == 1 ? EMPTY_INT_ARRAY : getSpecifier(unfavouredCodec, defaultForBand);
  257.             final int[] specifier = new int[1 + favouredSpecifier.length + unfavouredSpecifier.length + tokenSpecifier.length];
  258.             specifier[0] = first;
  259.             int index = 1;
  260.             for (final int element : favouredSpecifier) {
  261.                 specifier[index] = element;
  262.                 index++;
  263.             }
  264.             for (final int element : tokenSpecifier) {
  265.                 specifier[index] = element;
  266.                 index++;
  267.             }
  268.             for (final int element : unfavouredSpecifier) {
  269.                 specifier[index] = element;
  270.                 index++;
  271.             }
  272.             return specifier;
  273.         }

  274.         return null;
  275.     }

  276.     public static int getSpecifierForDefaultCodec(final BHSDCodec defaultCodec) {
  277.         return getSpecifier(defaultCodec, null)[0];
  278.     }
  279. }