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