    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.pack200;
    018
    019import java.io.EOFException;
    020import java.io.IOException;
    021import java.io.InputStream;
    022import java.util.Arrays;
    023import java.util.HashMap;
    024import java.util.Map;
    025
    026/**
    027 * CodecEncoding is used to get the right Codec for a given meta-encoding
    028 */
    029public class CodecEncoding {
    030
    031    /**
    032     * The canonical encodings are defined to allow a single byte to represent one of the standard encodings. The
    033     * following values are defined in the Pack200 specification, and this array cannot be changed.
    034     */
    035    private static final BHSDCodec[] canonicalCodec = {null, new BHSDCodec(1, 256), new BHSDCodec(1, 256, 1),
    036        new BHSDCodec(1, 256, 0, 1), new BHSDCodec(1, 256, 1, 1), new BHSDCodec(2, 256), new BHSDCodec(2, 256, 1),
    037        new BHSDCodec(2, 256, 0, 1), new BHSDCodec(2, 256, 1, 1), new BHSDCodec(3, 256), new BHSDCodec(3, 256, 1),
    038        new BHSDCodec(3, 256, 0, 1), new BHSDCodec(3, 256, 1, 1), new BHSDCodec(4, 256), new BHSDCodec(4, 256, 1),
    039        new BHSDCodec(4, 256, 0, 1), new BHSDCodec(4, 256, 1, 1), new BHSDCodec(5, 4), new BHSDCodec(5, 4, 1),
    040        new BHSDCodec(5, 4, 2), new BHSDCodec(5, 16), new BHSDCodec(5, 16, 1), new BHSDCodec(5, 16, 2),
    041        new BHSDCodec(5, 32), new BHSDCodec(5, 32, 1), new BHSDCodec(5, 32, 2), new BHSDCodec(5, 64),
    042        new BHSDCodec(5, 64, 1), new BHSDCodec(5, 64, 2), new BHSDCodec(5, 128), new BHSDCodec(5, 128, 1),
    043        new BHSDCodec(5, 128, 2), new BHSDCodec(5, 4, 0, 1), new BHSDCodec(5, 4, 1, 1), new BHSDCodec(5, 4, 2, 1),
    044        new BHSDCodec(5, 16, 0, 1), new BHSDCodec(5, 16, 1, 1), new BHSDCodec(5, 16, 2, 1), new BHSDCodec(5, 32, 0, 1),
    045        new BHSDCodec(5, 32, 1, 1), new BHSDCodec(5, 32, 2, 1), new BHSDCodec(5, 64, 0, 1), new BHSDCodec(5, 64, 1, 1),
    046        new BHSDCodec(5, 64, 2, 1), new BHSDCodec(5, 128, 0, 1), new BHSDCodec(5, 128, 1, 1),
    047        new BHSDCodec(5, 128, 2, 1), new BHSDCodec(2, 192), new BHSDCodec(2, 224), new BHSDCodec(2, 240),
    048        new BHSDCodec(2, 248), new BHSDCodec(2, 252), new BHSDCodec(2, 8, 0, 1), new BHSDCodec(2, 8, 1, 1),
    049        new BHSDCodec(2, 16, 0, 1), new BHSDCodec(2, 16, 1, 1), new BHSDCodec(2, 32, 0, 1), new BHSDCodec(2, 32, 1, 1),
    050        new BHSDCodec(2, 64, 0, 1), new BHSDCodec(2, 64, 1, 1), new BHSDCodec(2, 128, 0, 1),
    051        new BHSDCodec(2, 128, 1, 1), new BHSDCodec(2, 192, 0, 1), new BHSDCodec(2, 192, 1, 1),
    052        new BHSDCodec(2, 224, 0, 1), new BHSDCodec(2, 224, 1, 1), new BHSDCodec(2, 240, 0, 1),
    053        new BHSDCodec(2, 240, 1, 1), new BHSDCodec(2, 248, 0, 1), new BHSDCodec(2, 248, 1, 1), new BHSDCodec(3, 192),
    054        new BHSDCodec(3, 224), new BHSDCodec(3, 240), new BHSDCodec(3, 248), new BHSDCodec(3, 252),
    055        new BHSDCodec(3, 8, 0, 1), new BHSDCodec(3, 8, 1, 1), new BHSDCodec(3, 16, 0, 1), new BHSDCodec(3, 16, 1, 1),
    056        new BHSDCodec(3, 32, 0, 1), new BHSDCodec(3, 32, 1, 1), new BHSDCodec(3, 64, 0, 1), new BHSDCodec(3, 64, 1, 1),
    057        new BHSDCodec(3, 128, 0, 1), new BHSDCodec(3, 128, 1, 1), new BHSDCodec(3, 192, 0, 1),
    058        new BHSDCodec(3, 192, 1, 1), new BHSDCodec(3, 224, 0, 1), new BHSDCodec(3, 224, 1, 1),
    059        new BHSDCodec(3, 240, 0, 1), new BHSDCodec(3, 240, 1, 1), new BHSDCodec(3, 248, 0, 1),
    060        new BHSDCodec(3, 248, 1, 1), new BHSDCodec(4, 192), new BHSDCodec(4, 224), new BHSDCodec(4, 240),
    061        new BHSDCodec(4, 248), new BHSDCodec(4, 252), new BHSDCodec(4, 8, 0, 1), new BHSDCodec(4, 8, 1, 1),
    062        new BHSDCodec(4, 16, 0, 1), new BHSDCodec(4, 16, 1, 1), new BHSDCodec(4, 32, 0, 1), new BHSDCodec(4, 32, 1, 1),
    063        new BHSDCodec(4, 64, 0, 1), new BHSDCodec(4, 64, 1, 1), new BHSDCodec(4, 128, 0, 1),
    064        new BHSDCodec(4, 128, 1, 1), new BHSDCodec(4, 192, 0, 1), new BHSDCodec(4, 192, 1, 1),
    065        new BHSDCodec(4, 224, 0, 1), new BHSDCodec(4, 224, 1, 1), new BHSDCodec(4, 240, 0, 1),
    066        new BHSDCodec(4, 240, 1, 1), new BHSDCodec(4, 248, 0, 1), new BHSDCodec(4, 248, 1, 1)};
    067
    068    private static Map<BHSDCodec, Integer> canonicalCodecsToSpecifiers;
    069
    070    static {
    071        final HashMap<BHSDCodec, Integer> reverseMap = new HashMap<>(canonicalCodec.length);
    072        for (int i = 0; i < canonicalCodec.length; i++) {
    073            reverseMap.put(canonicalCodec[i], Integer.valueOf(i));
    074        }
    075        canonicalCodecsToSpecifiers = reverseMap;
    076    }
    077
    078    public static BHSDCodec getCanonicalCodec(final int i) {
    079        return canonicalCodec[i];
    080    }
    081
    082    /**
    083     * Returns the codec specified by the given value byte and optional byte header. If the value is &gt;= 116, then
    084     * bytes may be consumed from the secondary input stream, which is taken to be the contents of the band_headers byte
    085     * array. Since the values from this are consumed and not repeated, the input stream should be reused for subsequent
    086     * encodings. This does not therefore close the input stream.
    087     *
    088     * @param value the canonical encoding value
    089     * @param in the input stream to read additional byte headers from
    090     * @param defaultCodec TODO
    091     * @return the corresponding codec, or {@code null} if the default should be used
    092     *
    093     * @throws IOException if there is a problem reading from the input stream (which in reality, is never, since the
    094     *         band_headers are likely stored in a byte array and accessed via a ByteArrayInputStream. However, an
    095     *         EOFException could occur if things go wrong)
    096     * @throws Pack200Exception TODO
    097     */
    098    public static Codec getCodec(final int value, final InputStream in, final Codec defaultCodec)
    099        throws IOException, Pack200Exception {
    100        // Sanity check to make sure that no-one has changed
    101        // the canonical codecs, which would really cause havoc
    102        if (canonicalCodec.length != 116) {
    103            throw new Error("Canonical encodings have been incorrectly modified");
    104        }
    105        if (value < 0) {
    106            throw new IllegalArgumentException("Encoding cannot be less than zero");
    107        }
    108        if (value == 0) {
    109            return defaultCodec;
    110        }
    111        if (value <= 115) {
    112            return canonicalCodec[value];
    113        }
    114        if (value == 116) {
    115            int code = in.read();
    116            if (code == -1) {
    117                throw new EOFException("End of buffer read whilst trying to decode codec");
    118            }
    119            final int d = code & 0x01;
    120            final int s = code >> 1 & 0x03;
    121            final int b = (code >> 3 & 0x07) + 1; // this might result in an invalid
    122            // number, but it's checked in the
    123            // Codec constructor
    124            code = in.read();
    125            if (code == -1) {
    126                throw new EOFException("End of buffer read whilst trying to decode codec");
    127            }
    128            final int h = code + 1;
    129            // This handles the special cases for invalid combinations of data.
    130            return new BHSDCodec(b, h, s, d);
    131        }
    132        if (value >= 117 && value <= 140) { // Run codec
    133            final int offset = value - 117;
    134            final int kx = offset & 3;
    135            final boolean kbflag = (offset >> 2 & 1) == 1;
    136            final boolean adef = (offset >> 3 & 1) == 1;
    137            final boolean bdef = (offset >> 4 & 1) == 1;
    138            // If both A and B use the default encoding, what's the point of
    139            // having a run of default values followed by default values
    140            if (adef && bdef) {
    141                throw new Pack200Exception("ADef and BDef should never both be true");
    142            }
    143            final int kb = kbflag ? in.read() : 3;
    144            final int k = (kb + 1) * (int) Math.pow(16, kx);
    145            Codec aCodec, bCodec;
    146            if (adef) {
    147                aCodec = defaultCodec;
    148            } else {
    149                aCodec = getCodec(in.read(), in, defaultCodec);
    150            }
    151            if (bdef) {
    152                bCodec = defaultCodec;
    153            } else {
    154                bCodec = getCodec(in.read(), in, defaultCodec);
    155            }
    156            return new RunCodec(k, aCodec, bCodec);
    157        }
    158        if (value < 141 || value > 188) {
    159            throw new Pack200Exception("Invalid codec encoding byte (" + value + ") found");
    160        }
    161        final int offset = value - 141;
    162        final boolean fdef = (offset & 1) == 1;
    163        final boolean udef = (offset >> 1 & 1) == 1;
    164        final int tdefl = offset >> 2;
    165        final boolean tdef = tdefl != 0;
    166        // From section 6.7.3 of spec
    167        final int[] tdefToL = {0, 4, 8, 16, 32, 64, 128, 192, 224, 240, 248, 252};
    168        final int l = tdefToL[tdefl];
    169        // NOTE: Do not re-factor this to bring out uCodec; the order in
    170        // which
    171        // they are read from the stream is important
    172        if (tdef) {
    173            final Codec fCodec = fdef ? defaultCodec : getCodec(in.read(), in, defaultCodec);
    174            final Codec uCodec = udef ? defaultCodec : getCodec(in.read(), in, defaultCodec);
    175            // Unfortunately, if tdef, then tCodec depends both on l and
    176            // also on k, the
    177            // number of items read from the fCodec. So we don't know in
    178            // advance what
    179            // the codec will be.
    180            return new PopulationCodec(fCodec, l, uCodec);
    181        }
    182        final Codec fCodec = fdef ? defaultCodec : getCodec(in.read(), in, defaultCodec);
    183        final Codec tCodec = getCodec(in.read(), in, defaultCodec);
    184        final Codec uCodec = udef ? defaultCodec : getCodec(in.read(), in, defaultCodec);
    185        return new PopulationCodec(fCodec, tCodec, uCodec);
    186    }
    187
    188    public static int[] getSpecifier(final Codec codec, final Codec defaultForBand) {
    189                if (canonicalCodecsToSpecifiers.containsKey(codec)) {
    190                        return new int[] { canonicalCodecsToSpecifiers.get(codec).intValue() };
    191                }
    192        if (codec instanceof BHSDCodec) {
    193            // Cache these?
    194            final BHSDCodec bhsdCodec = (BHSDCodec) codec;
    195            final int[] specifiers = new int[3];
    196            specifiers[0] = 116;
    197            specifiers[1] = (bhsdCodec.isDelta() ? 1 : 0) + 2 * bhsdCodec.getS() + 8 * (bhsdCodec.getB() - 1);
    198            specifiers[2] = bhsdCodec.getH() - 1;
    199            return specifiers;
    200        }
    201        if (codec instanceof RunCodec) {
    202            final RunCodec runCodec = (RunCodec) codec;
    203            final int k = runCodec.getK();
    204            int kb;
    205            int kx;
    206            if (k <= 256) {
    207                kb = 0;
    208                kx = k - 1;
    209            } else if (k <= 4096) {
    210                kb = 1;
    211                kx = k / 16 - 1;
    212            } else if (k <= 65536) {
    213                kb = 2;
    214                kx = k / 256 - 1;
    215            } else {
    216                kb = 3;
    217                kx = k / 4096 - 1;
    218            }
    219            final Codec aCodec = runCodec.getACodec();
    220            final Codec bCodec = runCodec.getBCodec();
    221            int abDef = 0;
    222            if (aCodec.equals(defaultForBand)) {
    223                abDef = 1;
    224            } else if (bCodec.equals(defaultForBand)) {
    225                abDef = 2;
    226            }
    227            final int first = 117 + kb + (kx == 3 ? 0 : 4) + 8 * abDef;
    228            final int[] aSpecifier = abDef == 1 ? new int[0] : getSpecifier(aCodec, defaultForBand);
    229            final int[] bSpecifier = abDef == 2 ? new int[0] : getSpecifier(bCodec, defaultForBand);
    230            final int[] specifier = new int[1 + (kx == 3 ? 0 : 1) + aSpecifier.length + bSpecifier.length];
    231            specifier[0] = first;
    232            int index = 1;
    233            if (kx != 3) {
    234                specifier[1] = kx;
    235                index++;
    236            }
    237            for (final int element : aSpecifier) {
    238                specifier[index] = element;
    239                index++;
    240            }
    241            for (final int element : bSpecifier) {
    242                specifier[index] = element;
    243                index++;
    244            }
    245            return specifier;
    246        }
    247        if (codec instanceof PopulationCodec) {
    248            final PopulationCodec populationCodec = (PopulationCodec) codec;
    249            final Codec tokenCodec = populationCodec.getTokenCodec();
    250            final Codec favouredCodec = populationCodec.getFavouredCodec();
    251            final Codec unfavouredCodec = populationCodec.getUnfavouredCodec();
    252            final int fDef = favouredCodec.equals(defaultForBand) ? 1 : 0;
    253            final int uDef = unfavouredCodec.equals(defaultForBand) ? 1 : 0;
    254            int tDefL = 0;
    255            final int[] favoured = populationCodec.getFavoured();
    256            if (favoured != null) {
    257                if (tokenCodec == Codec.BYTE1) {
    258                    tDefL = 1;
    259                } else if (tokenCodec instanceof BHSDCodec) {
    260                    final BHSDCodec tokenBHSD = (BHSDCodec) tokenCodec;
    261                    if (tokenBHSD.getS() == 0) {
    262                        final int[] possibleLValues = {4, 8, 16, 32, 64, 128, 192, 224, 240, 248, 252};
    263                        final int l = 256 - tokenBHSD.getH();
    264                        int index = Arrays.binarySearch(possibleLValues, l);
    265                        if (index != -1) {
    266                            // TODO: check range is ok for ks
    267                            tDefL = index++;
    268                        }
    269                    }
    270                }
    271            }
    272            final int first = 141 + fDef + 2 * uDef + 4 * tDefL;
    273            final int[] favouredSpecifier = fDef == 1 ? new int[0] : getSpecifier(favouredCodec, defaultForBand);
    274            final int[] tokenSpecifier = tDefL != 0 ? new int[0] : getSpecifier(tokenCodec, defaultForBand);
    275            final int[] unfavouredSpecifier = uDef == 1 ? new int[0] : getSpecifier(unfavouredCodec, defaultForBand);
    276            final int[] specifier = new int[1 + favouredSpecifier.length + unfavouredSpecifier.length
    277                + tokenSpecifier.length];
    278            specifier[0] = first;
    279            int index = 1;
    280            for (final int element : favouredSpecifier) {
    281                specifier[index] = element;
    282                index++;
    283            }
    284            for (final int element : tokenSpecifier) {
    285                specifier[index] = element;
    286                index++;
    287            }
    288            for (final int element : unfavouredSpecifier) {
    289                specifier[index] = element;
    290                index++;
    291            }
    292            return specifier;
    293        }
    294
    295        return null;
    296    }
    297
    298    public static int getSpecifierForDefaultCodec(final BHSDCodec defaultCodec) {
    299        return getSpecifier(defaultCodec, null)[0];
    300    }
    301}