View Javadoc
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  
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.util.Arrays;
22  
23  import org.apache.commons.compress.utils.ExactMath;
24  
25  /**
26   * 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
27   * that since this codec maintains state, the instances are not reusable.
28   */
29  public class RunCodec extends Codec {
30  
31      private int k;
32      private final Codec aCodec;
33      private final Codec bCodec;
34      private int last;
35  
36      public RunCodec(final int k, final Codec aCodec, final Codec bCodec) throws Pack200Exception {
37          if (k <= 0) {
38              throw new Pack200Exception("Cannot have a RunCodec for a negative number of numbers");
39          }
40          if (aCodec == null || bCodec == null) {
41              throw new Pack200Exception("Must supply both codecs for a RunCodec");
42          }
43          this.k = k;
44          this.aCodec = aCodec;
45          this.bCodec = bCodec;
46      }
47  
48      @Override
49      public int decode(final InputStream in) throws IOException, Pack200Exception {
50          return decode(in, this.last);
51      }
52  
53      @Override
54      public int decode(final InputStream in, final long last) throws IOException, Pack200Exception {
55          if (--k >= 0) {
56              final int value = aCodec.decode(in, this.last);
57              this.last = k == 0 ? 0 : value;
58              return normalise(value, aCodec);
59          }
60          this.last = bCodec.decode(in, this.last);
61          return normalise(this.last, bCodec);
62      }
63  
64      @Override
65      public int[] decodeInts(final int n, final InputStream in) throws IOException, Pack200Exception {
66          final int[] aValues = aCodec.decodeInts(k, in);
67          normalise(aValues, aCodec);
68          final int[] bValues = bCodec.decodeInts(n - k, in);
69          normalise(bValues, bCodec);
70          final int[] band = new int[check(n, in)];
71          System.arraycopy(aValues, 0, band, 0, k);
72          System.arraycopy(bValues, 0, band, k, n - k);
73          lastBandLength = aCodec.lastBandLength + bCodec.lastBandLength;
74          return band;
75      }
76  
77      @Override
78      public byte[] encode(final int value) throws Pack200Exception {
79          throw new Pack200Exception("Must encode entire band at once with a RunCodec");
80      }
81  
82      @Override
83      public byte[] encode(final int value, final int last) throws Pack200Exception {
84          throw new Pack200Exception("Must encode entire band at once with a RunCodec");
85      }
86  
87      public Codec getACodec() {
88          return aCodec;
89      }
90  
91      public Codec getBCodec() {
92          return bCodec;
93      }
94  
95      public int getK() {
96          return k;
97      }
98  
99      private int normalise(int value, final Codec codecUsed) {
100         if (codecUsed instanceof BHSDCodec) {
101             final BHSDCodec bhsd = (BHSDCodec) codecUsed;
102             if (bhsd.isDelta()) {
103                 final long cardinality = bhsd.cardinality();
104                 while (value > bhsd.largest()) {
105                     value -= cardinality;
106                 }
107                 while (value < bhsd.smallest()) {
108                     value = ExactMath.add(value, cardinality);
109                 }
110             }
111         }
112         return value;
113     }
114 
115     private void normalise(final int[] band, final Codec codecUsed) {
116         if (codecUsed instanceof BHSDCodec) {
117             final BHSDCodec bhsd = (BHSDCodec) codecUsed;
118             if (bhsd.isDelta()) {
119                 final long cardinality = bhsd.cardinality();
120                 for (int i = 0; i < band.length; i++) {
121                     while (band[i] > bhsd.largest()) {
122                         band[i] -= cardinality;
123                     }
124                     while (band[i] < bhsd.smallest()) {
125                         band[i] = ExactMath.add(band[i], cardinality);
126                     }
127                 }
128             }
129         } else if (codecUsed instanceof PopulationCodec) {
130             final PopulationCodec popCodec = (PopulationCodec) codecUsed;
131             final int[] favoured = popCodec.getFavoured().clone();
132             Arrays.sort(favoured);
133             for (int i = 0; i < band.length; i++) {
134                 final boolean favouredValue = Arrays.binarySearch(favoured, band[i]) > -1;
135                 final Codec theCodec = favouredValue ? popCodec.getFavouredCodec() : popCodec.getUnfavouredCodec();
136                 if (theCodec instanceof BHSDCodec) {
137                     final BHSDCodec bhsd = (BHSDCodec) theCodec;
138                     if (bhsd.isDelta()) {
139                         final long cardinality = bhsd.cardinality();
140                         while (band[i] > bhsd.largest()) {
141                             band[i] -= cardinality;
142                         }
143                         while (band[i] < bhsd.smallest()) {
144                             band[i] = ExactMath.add(band[i], cardinality);
145                         }
146                     }
147                 }
148             }
149         }
150     }
151 
152     @Override
153     public String toString() {
154         return "RunCodec[k=" + k + ";aCodec=" + aCodec + "bCodec=" + bCodec + "]";
155     }
156 }