RunCodec.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.IOException;
  19. import java.io.InputStream;
  20. import java.util.Arrays;

  21. import org.apache.commons.compress.utils.ExactMath;

  22. /**
  23.  * A run codec is a grouping of two nested codecs; K values are decoded from the first codec, and the remaining codes are decoded from the remaining codec. Note
  24.  * that since this codec maintains state, the instances are not reusable.
  25.  */
  26. public class RunCodec extends Codec {

  27.     private int k;
  28.     private final Codec aCodec;
  29.     private final Codec bCodec;
  30.     private int last;

  31.     public RunCodec(final int k, final Codec aCodec, final Codec bCodec) throws Pack200Exception {
  32.         if (k <= 0) {
  33.             throw new Pack200Exception("Cannot have a RunCodec for a negative number of numbers");
  34.         }
  35.         if (aCodec == null || bCodec == null) {
  36.             throw new Pack200Exception("Must supply both codecs for a RunCodec");
  37.         }
  38.         this.k = k;
  39.         this.aCodec = aCodec;
  40.         this.bCodec = bCodec;
  41.     }

  42.     @Override
  43.     public int decode(final InputStream in) throws IOException, Pack200Exception {
  44.         return decode(in, this.last);
  45.     }

  46.     @Override
  47.     public int decode(final InputStream in, final long last) throws IOException, Pack200Exception {
  48.         if (--k >= 0) {
  49.             final int value = aCodec.decode(in, this.last);
  50.             this.last = k == 0 ? 0 : value;
  51.             return normalise(value, aCodec);
  52.         }
  53.         this.last = bCodec.decode(in, this.last);
  54.         return normalise(this.last, bCodec);
  55.     }

  56.     @Override
  57.     public int[] decodeInts(final int n, final InputStream in) throws IOException, Pack200Exception {
  58.         final int[] aValues = aCodec.decodeInts(k, in);
  59.         normalise(aValues, aCodec);
  60.         final int[] bValues = bCodec.decodeInts(n - k, in);
  61.         normalise(bValues, bCodec);
  62.         final int[] band = new int[check(n, in)];
  63.         System.arraycopy(aValues, 0, band, 0, k);
  64.         System.arraycopy(bValues, 0, band, k, n - k);
  65.         lastBandLength = aCodec.lastBandLength + bCodec.lastBandLength;
  66.         return band;
  67.     }

  68.     @Override
  69.     public byte[] encode(final int value) throws Pack200Exception {
  70.         throw new Pack200Exception("Must encode entire band at once with a RunCodec");
  71.     }

  72.     @Override
  73.     public byte[] encode(final int value, final int last) throws Pack200Exception {
  74.         throw new Pack200Exception("Must encode entire band at once with a RunCodec");
  75.     }

  76.     public Codec getACodec() {
  77.         return aCodec;
  78.     }

  79.     public Codec getBCodec() {
  80.         return bCodec;
  81.     }

  82.     public int getK() {
  83.         return k;
  84.     }

  85.     private int normalise(int value, final Codec codecUsed) {
  86.         if (codecUsed instanceof BHSDCodec) {
  87.             final BHSDCodec bhsd = (BHSDCodec) codecUsed;
  88.             if (bhsd.isDelta()) {
  89.                 final long cardinality = bhsd.cardinality();
  90.                 while (value > bhsd.largest()) {
  91.                     value -= cardinality;
  92.                 }
  93.                 while (value < bhsd.smallest()) {
  94.                     value = ExactMath.add(value, cardinality);
  95.                 }
  96.             }
  97.         }
  98.         return value;
  99.     }

  100.     private void normalise(final int[] band, final Codec codecUsed) {
  101.         if (codecUsed instanceof BHSDCodec) {
  102.             final BHSDCodec bhsd = (BHSDCodec) codecUsed;
  103.             if (bhsd.isDelta()) {
  104.                 final long cardinality = bhsd.cardinality();
  105.                 for (int i = 0; i < band.length; i++) {
  106.                     while (band[i] > bhsd.largest()) {
  107.                         band[i] -= cardinality;
  108.                     }
  109.                     while (band[i] < bhsd.smallest()) {
  110.                         band[i] = ExactMath.add(band[i], cardinality);
  111.                     }
  112.                 }
  113.             }
  114.         } else if (codecUsed instanceof PopulationCodec) {
  115.             final PopulationCodec popCodec = (PopulationCodec) codecUsed;
  116.             final int[] favoured = popCodec.getFavoured().clone();
  117.             Arrays.sort(favoured);
  118.             for (int i = 0; i < band.length; i++) {
  119.                 final boolean favouredValue = Arrays.binarySearch(favoured, band[i]) > -1;
  120.                 final Codec theCodec = favouredValue ? popCodec.getFavouredCodec() : popCodec.getUnfavouredCodec();
  121.                 if (theCodec instanceof BHSDCodec) {
  122.                     final BHSDCodec bhsd = (BHSDCodec) theCodec;
  123.                     if (bhsd.isDelta()) {
  124.                         final long cardinality = bhsd.cardinality();
  125.                         while (band[i] > bhsd.largest()) {
  126.                             band[i] -= cardinality;
  127.                         }
  128.                         while (band[i] < bhsd.smallest()) {
  129.                             band[i] = ExactMath.add(band[i], cardinality);
  130.                         }
  131.                     }
  132.                 }
  133.             }
  134.         }
  135.     }

  136.     @Override
  137.     public String toString() {
  138.         return "RunCodec[k=" + k + ";aCodec=" + aCodec + "bCodec=" + bCodec + "]";
  139.     }
  140. }