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