    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.IOException;
    020import java.io.InputStream;
    021import java.util.Arrays;
    022
    023/**
    024 * A run codec is a grouping of two nested codecs; K values are decoded from the first codec, and the remaining codes
    025 * are decoded from the remaining codec. Note that since this codec maintains state, the instances are not reusable.
    026 */
    027public class RunCodec extends Codec {
    028
    029    private int k;
    030    private final Codec aCodec;
    031    private final Codec bCodec;
    032    private int last;
    033
    034    public RunCodec(final int k, final Codec aCodec, final Codec bCodec) throws Pack200Exception {
    035        if (k <= 0) {
    036            throw new Pack200Exception("Cannot have a RunCodec for a negative number of numbers");
    037        }
    038        if (aCodec == null || bCodec == null) {
    039            throw new Pack200Exception("Must supply both codecs for a RunCodec");
    040        }
    041        this.k = k;
    042        this.aCodec = aCodec;
    043        this.bCodec = bCodec;
    044    }
    045
    046    @Override
    047    public int decode(final InputStream in) throws IOException, Pack200Exception {
    048        return decode(in, this.last);
    049    }
    050
    051    @Override
    052    public int decode(final InputStream in, final long last) throws IOException, Pack200Exception {
    053        if (--k >= 0) {
    054            final int value = aCodec.decode(in, this.last);
    055            this.last = (k == 0 ? 0 : value);
    056            return normalise(value, aCodec);
    057        }
    058        this.last = bCodec.decode(in, this.last);
    059        return normalise(this.last, bCodec);
    060    }
    061
    062    private int normalise(int value, final Codec codecUsed) {
    063        if (codecUsed instanceof BHSDCodec) {
    064            final BHSDCodec bhsd = (BHSDCodec) codecUsed;
    065            if (bhsd.isDelta()) {
    066                final long cardinality = bhsd.cardinality();
    067                while (value > bhsd.largest()) {
    068                    value -= cardinality;
    069                }
    070                while (value < bhsd.smallest()) {
    071                    value += cardinality;
    072                }
    073            }
    074        }
    075        return value;
    076    }
    077
    078    @Override
    079    public int[] decodeInts(final int n, final InputStream in) throws IOException, Pack200Exception {
    080        final int[] band = new int[n];
    081        final int[] aValues = aCodec.decodeInts(k, in);
    082        normalise(aValues, aCodec);
    083        final int[] bValues = bCodec.decodeInts(n - k, in);
    084        normalise(bValues, bCodec);
    085        System.arraycopy(aValues, 0, band, 0, k);
    086        System.arraycopy(bValues, 0, band, k, n - k);
    087        lastBandLength = aCodec.lastBandLength + bCodec.lastBandLength;
    088        return band;
    089    }
    090
    091    private void normalise(final int[] band, final Codec codecUsed) {
    092        if (codecUsed instanceof BHSDCodec) {
    093            final BHSDCodec bhsd = (BHSDCodec) codecUsed;
    094            if (bhsd.isDelta()) {
    095                final long cardinality = bhsd.cardinality();
    096                for (int i = 0; i < band.length; i++) {
    097                    while (band[i] > bhsd.largest()) {
    098                        band[i] -= cardinality;
    099                    }
    100                    while (band[i] < bhsd.smallest()) {
    101                        band[i] += cardinality;
    102                    }
    103                }
    104            }
    105        } else if (codecUsed instanceof PopulationCodec) {
    106            final PopulationCodec popCodec = (PopulationCodec) codecUsed;
    107            final int[] favoured = (int[]) popCodec.getFavoured().clone();
    108            Arrays.sort(favoured);
    109            for (int i = 0; i < band.length; i++) {
    110                final boolean favouredValue = Arrays.binarySearch(favoured, band[i]) > -1;
    111                final Codec theCodec = favouredValue ? popCodec.getFavouredCodec() : popCodec.getUnfavouredCodec();
    112                if (theCodec instanceof BHSDCodec) {
    113                    final BHSDCodec bhsd = (BHSDCodec) theCodec;
    114                    if (bhsd.isDelta()) {
    115                        final long cardinality = bhsd.cardinality();
    116                        while (band[i] > bhsd.largest()) {
    117                            band[i] -= cardinality;
    118                        }
    119                        while (band[i] < bhsd.smallest()) {
    120                            band[i] += cardinality;
    121                        }
    122                    }
    123                }
    124            }
    125        }
    126    }
    127
    128    @Override
    129    public String toString() {
    130        return "RunCodec[k=" + k + ";aCodec=" + aCodec + "bCodec=" + bCodec + "]";
    131    }
    132
    133    @Override
    134    public byte[] encode(final int value, final int last) throws Pack200Exception {
    135        throw new Pack200Exception("Must encode entire band at once with a RunCodec");
    136    }
    137
    138    @Override
    139    public byte[] encode(final int value) throws Pack200Exception {
    140        throw new Pack200Exception("Must encode entire band at once with a RunCodec");
    141    }
    142
    143    public int getK() {
    144        return k;
    145    }
    146
    147    public Codec getACodec() {
    148        return aCodec;
    149    }
    150
    151    public Codec getBCodec() {
    152        return bCodec;
    153    }
    154}