View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.commons.compress.harmony.pack200;
20  
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.util.Arrays;
24  
25  import org.apache.commons.compress.utils.ExactMath;
26  
27  /**
28   * 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
29   * that since this codec maintains state, the instances are not reusable.
30   */
31  public class RunCodec extends Codec {
32  
33      private int k;
34      private final Codec aCodec;
35      private final Codec bCodec;
36      private int last;
37  
38      public RunCodec(final int k, final Codec aCodec, final Codec bCodec) throws Pack200Exception {
39          if (k <= 0) {
40              throw new Pack200Exception("Cannot have a RunCodec for a negative number of numbers");
41          }
42          if (aCodec == null || bCodec == null) {
43              throw new Pack200Exception("Must supply both codecs for a RunCodec");
44          }
45          this.k = k;
46          this.aCodec = aCodec;
47          this.bCodec = bCodec;
48      }
49  
50      @Override
51      public int decode(final InputStream in) throws IOException, Pack200Exception {
52          return decode(in, this.last);
53      }
54  
55      @Override
56      public int decode(final InputStream in, final long last) throws IOException, Pack200Exception {
57          if (--k >= 0) {
58              final int value = aCodec.decode(in, this.last);
59              this.last = k == 0 ? 0 : value;
60              return normalise(value, aCodec);
61          }
62          this.last = bCodec.decode(in, this.last);
63          return normalise(this.last, bCodec);
64      }
65  
66      @Override
67      public int[] decodeInts(final int n, final InputStream in) throws IOException, Pack200Exception {
68          final int[] aValues = aCodec.decodeInts(k, in);
69          normalise(aValues, aCodec);
70          final int[] bValues = bCodec.decodeInts(n - k, in);
71          normalise(bValues, bCodec);
72          final int[] band = new int[check(n, in)];
73          System.arraycopy(aValues, 0, band, 0, k);
74          System.arraycopy(bValues, 0, band, k, n - k);
75          lastBandLength = aCodec.lastBandLength + bCodec.lastBandLength;
76          return band;
77      }
78  
79      @Override
80      public byte[] encode(final int value) throws Pack200Exception {
81          throw new Pack200Exception("Must encode entire band at once with a RunCodec");
82      }
83  
84      @Override
85      public byte[] encode(final int value, final int last) throws Pack200Exception {
86          throw new Pack200Exception("Must encode entire band at once with a RunCodec");
87      }
88  
89      public Codec getACodec() {
90          return aCodec;
91      }
92  
93      public Codec getBCodec() {
94          return bCodec;
95      }
96  
97      public int getK() {
98          return k;
99      }
100 
101     private int normalise(int value, final Codec codecUsed) {
102         if (codecUsed instanceof BHSDCodec) {
103             final BHSDCodec bhsd = (BHSDCodec) codecUsed;
104             if (bhsd.isDelta()) {
105                 final long cardinality = bhsd.cardinality();
106                 while (value > bhsd.largest()) {
107                     value -= cardinality;
108                 }
109                 while (value < bhsd.smallest()) {
110                     value = ExactMath.add(value, cardinality);
111                 }
112             }
113         }
114         return value;
115     }
116 
117     private void normalise(final int[] band, final Codec codecUsed) {
118         if (codecUsed instanceof BHSDCodec) {
119             final BHSDCodec bhsd = (BHSDCodec) codecUsed;
120             if (bhsd.isDelta()) {
121                 final long cardinality = bhsd.cardinality();
122                 for (int i = 0; i < band.length; i++) {
123                     while (band[i] > bhsd.largest()) {
124                         band[i] -= cardinality;
125                     }
126                     while (band[i] < bhsd.smallest()) {
127                         band[i] = ExactMath.add(band[i], cardinality);
128                     }
129                 }
130             }
131         } else if (codecUsed instanceof PopulationCodec) {
132             final PopulationCodec popCodec = (PopulationCodec) codecUsed;
133             final int[] favoured = popCodec.getFavoured().clone();
134             Arrays.sort(favoured);
135             for (int i = 0; i < band.length; i++) {
136                 final boolean favouredValue = Arrays.binarySearch(favoured, band[i]) > -1;
137                 final Codec theCodec = favouredValue ? popCodec.getFavouredCodec() : popCodec.getUnfavouredCodec();
138                 if (theCodec instanceof BHSDCodec) {
139                     final BHSDCodec bhsd = (BHSDCodec) theCodec;
140                     if (bhsd.isDelta()) {
141                         final long cardinality = bhsd.cardinality();
142                         while (band[i] > bhsd.largest()) {
143                             band[i] -= cardinality;
144                         }
145                         while (band[i] < bhsd.smallest()) {
146                             band[i] = ExactMath.add(band[i], cardinality);
147                         }
148                     }
149                 }
150             }
151         }
152     }
153 
154     @Override
155     public String toString() {
156         return "RunCodec[k=" + k + ";aCodec=" + aCodec + "bCodec=" + bCodec + "]";
157     }
158 }