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.EOFException;
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.util.Arrays;
23  import java.util.HashMap;
24  import java.util.Map;
25  
26  /**
27   * CodecEncoding is used to get the right Codec for a given meta-encoding.
28   */
29  public class CodecEncoding {
30  
31      private static final int[] EMPTY_INT_ARRAY = {};
32  
33      /**
34       * The canonical encodings are defined to allow a single byte to represent one of the standard encodings. The following values are defined in the Pack200
35       * specification, and this array cannot be changed.
36       */
37      private static final BHSDCodec[] canonicalCodec = { null, new BHSDCodec(1, 256), new BHSDCodec(1, 256, 1), new BHSDCodec(1, 256, 0, 1),
38              new BHSDCodec(1, 256, 1, 1), new BHSDCodec(2, 256), new BHSDCodec(2, 256, 1), new BHSDCodec(2, 256, 0, 1), new BHSDCodec(2, 256, 1, 1),
39              new BHSDCodec(3, 256), new BHSDCodec(3, 256, 1), new BHSDCodec(3, 256, 0, 1), new BHSDCodec(3, 256, 1, 1), new BHSDCodec(4, 256),
40              new BHSDCodec(4, 256, 1), new BHSDCodec(4, 256, 0, 1), new BHSDCodec(4, 256, 1, 1), new BHSDCodec(5, 4), new BHSDCodec(5, 4, 1),
41              new BHSDCodec(5, 4, 2), new BHSDCodec(5, 16), new BHSDCodec(5, 16, 1), new BHSDCodec(5, 16, 2), new BHSDCodec(5, 32), new BHSDCodec(5, 32, 1),
42              new BHSDCodec(5, 32, 2), new BHSDCodec(5, 64), new BHSDCodec(5, 64, 1), new BHSDCodec(5, 64, 2), new BHSDCodec(5, 128), new BHSDCodec(5, 128, 1),
43              new BHSDCodec(5, 128, 2), new BHSDCodec(5, 4, 0, 1), new BHSDCodec(5, 4, 1, 1), new BHSDCodec(5, 4, 2, 1), new BHSDCodec(5, 16, 0, 1),
44              new BHSDCodec(5, 16, 1, 1), new BHSDCodec(5, 16, 2, 1), new BHSDCodec(5, 32, 0, 1), new BHSDCodec(5, 32, 1, 1), new BHSDCodec(5, 32, 2, 1),
45              new BHSDCodec(5, 64, 0, 1), new BHSDCodec(5, 64, 1, 1), new BHSDCodec(5, 64, 2, 1), new BHSDCodec(5, 128, 0, 1), new BHSDCodec(5, 128, 1, 1),
46              new BHSDCodec(5, 128, 2, 1), new BHSDCodec(2, 192), new BHSDCodec(2, 224), new BHSDCodec(2, 240), new BHSDCodec(2, 248), new BHSDCodec(2, 252),
47              new BHSDCodec(2, 8, 0, 1), new BHSDCodec(2, 8, 1, 1), new BHSDCodec(2, 16, 0, 1), new BHSDCodec(2, 16, 1, 1), new BHSDCodec(2, 32, 0, 1),
48              new BHSDCodec(2, 32, 1, 1), new BHSDCodec(2, 64, 0, 1), new BHSDCodec(2, 64, 1, 1), new BHSDCodec(2, 128, 0, 1), new BHSDCodec(2, 128, 1, 1),
49              new BHSDCodec(2, 192, 0, 1), new BHSDCodec(2, 192, 1, 1), new BHSDCodec(2, 224, 0, 1), new BHSDCodec(2, 224, 1, 1), new BHSDCodec(2, 240, 0, 1),
50              new BHSDCodec(2, 240, 1, 1), new BHSDCodec(2, 248, 0, 1), new BHSDCodec(2, 248, 1, 1), new BHSDCodec(3, 192), new BHSDCodec(3, 224),
51              new BHSDCodec(3, 240), new BHSDCodec(3, 248), new BHSDCodec(3, 252), new BHSDCodec(3, 8, 0, 1), new BHSDCodec(3, 8, 1, 1),
52              new BHSDCodec(3, 16, 0, 1), new BHSDCodec(3, 16, 1, 1), new BHSDCodec(3, 32, 0, 1), new BHSDCodec(3, 32, 1, 1), new BHSDCodec(3, 64, 0, 1),
53              new BHSDCodec(3, 64, 1, 1), new BHSDCodec(3, 128, 0, 1), new BHSDCodec(3, 128, 1, 1), new BHSDCodec(3, 192, 0, 1), new BHSDCodec(3, 192, 1, 1),
54              new BHSDCodec(3, 224, 0, 1), new BHSDCodec(3, 224, 1, 1), new BHSDCodec(3, 240, 0, 1), new BHSDCodec(3, 240, 1, 1), new BHSDCodec(3, 248, 0, 1),
55              new BHSDCodec(3, 248, 1, 1), new BHSDCodec(4, 192), new BHSDCodec(4, 224), new BHSDCodec(4, 240), new BHSDCodec(4, 248), new BHSDCodec(4, 252),
56              new BHSDCodec(4, 8, 0, 1), new BHSDCodec(4, 8, 1, 1), new BHSDCodec(4, 16, 0, 1), new BHSDCodec(4, 16, 1, 1), new BHSDCodec(4, 32, 0, 1),
57              new BHSDCodec(4, 32, 1, 1), new BHSDCodec(4, 64, 0, 1), new BHSDCodec(4, 64, 1, 1), new BHSDCodec(4, 128, 0, 1), new BHSDCodec(4, 128, 1, 1),
58              new BHSDCodec(4, 192, 0, 1), new BHSDCodec(4, 192, 1, 1), new BHSDCodec(4, 224, 0, 1), new BHSDCodec(4, 224, 1, 1), new BHSDCodec(4, 240, 0, 1),
59              new BHSDCodec(4, 240, 1, 1), new BHSDCodec(4, 248, 0, 1), new BHSDCodec(4, 248, 1, 1) };
60  
61      private static Map<BHSDCodec, Integer> canonicalCodecsToSpecifiers;
62  
63      static {
64          final HashMap<BHSDCodec, Integer> reverseMap = new HashMap<>(canonicalCodec.length);
65          for (int i = 0; i < canonicalCodec.length; i++) {
66              reverseMap.put(canonicalCodec[i], Integer.valueOf(i));
67          }
68          canonicalCodecsToSpecifiers = reverseMap;
69      }
70  
71      public static BHSDCodec getCanonicalCodec(final int i) {
72          return canonicalCodec[i];
73      }
74  
75      /**
76       * Gets the codec specified by the given value byte and optional byte header. If the value is &gt;= 116, then bytes may be consumed from the secondary
77       * input stream, which is taken to be the contents of the band_headers byte array. Since the values from this are consumed and not repeated, the input
78       * stream should be reused for subsequent encodings. This does not therefore close the input stream.
79       *
80       * @param value        the canonical encoding value
81       * @param in           the input stream to read additional byte headers from
82       * @param defaultCodec TODO
83       * @return the corresponding codec, or {@code null} if the default should be used
84       *
85       * @throws IOException      if there is a problem reading from the input stream (which in reality, is never, since the band_headers are likely stored in a
86       *                          byte array and accessed via a ByteArrayInputStream. However, an EOFException could occur if things go wrong)
87       * @throws Pack200Exception TODO
88       */
89      public static Codec getCodec(final int value, final InputStream in, final Codec defaultCodec) throws IOException, Pack200Exception {
90          // Sanity check to make sure that no-one has changed
91          // the canonical codecs, which would really cause havoc
92          if (canonicalCodec.length != 116) {
93              throw new Error("Canonical encodings have been incorrectly modified");
94          }
95          if (value < 0) {
96              throw new IllegalArgumentException("Encoding cannot be less than zero");
97          }
98          if (value == 0) {
99              return defaultCodec;
100         }
101         if (value <= 115) {
102             return canonicalCodec[value];
103         }
104         if (value == 116) {
105             int code = in.read();
106             if (code == -1) {
107                 throw new EOFException("End of buffer read whilst trying to decode codec");
108             }
109             final int d = code & 0x01;
110             final int s = code >> 1 & 0x03;
111             final int b = (code >> 3 & 0x07) + 1; // this might result in an invalid
112             // number, but it's checked in the
113             // Codec constructor
114             code = in.read();
115             if (code == -1) {
116                 throw new EOFException("End of buffer read whilst trying to decode codec");
117             }
118             final int h = code + 1;
119             // This handles the special cases for invalid combinations of data.
120             return new BHSDCodec(b, h, s, d);
121         }
122         if (value >= 117 && value <= 140) { // Run codec
123             final int offset = value - 117;
124             final int kx = offset & 3;
125             final boolean kbflag = (offset >> 2 & 1) == 1;
126             final boolean adef = (offset >> 3 & 1) == 1;
127             final boolean bdef = (offset >> 4 & 1) == 1;
128             // If both A and B use the default encoding, what's the point of
129             // having a run of default values followed by default values
130             if (adef && bdef) {
131                 throw new Pack200Exception("ADef and BDef should never both be true");
132             }
133             final int kb = kbflag ? in.read() : 3;
134             final int k = (kb + 1) * (int) Math.pow(16, kx);
135             Codec aCodec, bCodec;
136             if (adef) {
137                 aCodec = defaultCodec;
138             } else {
139                 aCodec = getCodec(in.read(), in, defaultCodec);
140             }
141             if (bdef) {
142                 bCodec = defaultCodec;
143             } else {
144                 bCodec = getCodec(in.read(), in, defaultCodec);
145             }
146             return new RunCodec(k, aCodec, bCodec);
147         }
148         if (value < 141 || value > 188) {
149             throw new Pack200Exception("Invalid codec encoding byte (" + value + ") found");
150         }
151         final int offset = value - 141;
152         final boolean fdef = (offset & 1) == 1;
153         final boolean udef = (offset >> 1 & 1) == 1;
154         final int tdefl = offset >> 2;
155         final boolean tdef = tdefl != 0;
156         // From section 6.7.3 of spec
157         final int[] tdefToL = { 0, 4, 8, 16, 32, 64, 128, 192, 224, 240, 248, 252 };
158         final int l = tdefToL[tdefl];
159         // NOTE: Do not re-factor this to bring out uCodec; the order in
160         // which
161         // they are read from the stream is important
162         if (tdef) {
163             final Codec fCodec = fdef ? defaultCodec : getCodec(in.read(), in, defaultCodec);
164             final Codec uCodec = udef ? defaultCodec : getCodec(in.read(), in, defaultCodec);
165             // Unfortunately, if tdef, then tCodec depends both on l and
166             // also on k, the
167             // number of items read from the fCodec. So we don't know in
168             // advance what
169             // the codec will be.
170             return new PopulationCodec(fCodec, l, uCodec);
171         }
172         final Codec fCodec = fdef ? defaultCodec : getCodec(in.read(), in, defaultCodec);
173         final Codec tCodec = getCodec(in.read(), in, defaultCodec);
174         final Codec uCodec = udef ? defaultCodec : getCodec(in.read(), in, defaultCodec);
175         return new PopulationCodec(fCodec, tCodec, uCodec);
176     }
177 
178     public static int[] getSpecifier(final Codec codec, final Codec defaultForBand) {
179         if (canonicalCodecsToSpecifiers.containsKey(codec)) {
180             return new int[] { canonicalCodecsToSpecifiers.get(codec).intValue() };
181         }
182         if (codec instanceof BHSDCodec) {
183             // Cache these?
184             final BHSDCodec bhsdCodec = (BHSDCodec) codec;
185             final int[] specifiers = new int[3];
186             specifiers[0] = 116;
187             specifiers[1] = (bhsdCodec.isDelta() ? 1 : 0) + 2 * bhsdCodec.getS() + 8 * (bhsdCodec.getB() - 1);
188             specifiers[2] = bhsdCodec.getH() - 1;
189             return specifiers;
190         }
191         if (codec instanceof RunCodec) {
192             final RunCodec runCodec = (RunCodec) codec;
193             final int k = runCodec.getK();
194             int kb;
195             int kx;
196             if (k <= 256) {
197                 kb = 0;
198                 kx = k - 1;
199             } else if (k <= 4096) {
200                 kb = 1;
201                 kx = k / 16 - 1;
202             } else if (k <= 65536) {
203                 kb = 2;
204                 kx = k / 256 - 1;
205             } else {
206                 kb = 3;
207                 kx = k / 4096 - 1;
208             }
209             final Codec aCodec = runCodec.getACodec();
210             final Codec bCodec = runCodec.getBCodec();
211             int abDef = 0;
212             if (aCodec.equals(defaultForBand)) {
213                 abDef = 1;
214             } else if (bCodec.equals(defaultForBand)) {
215                 abDef = 2;
216             }
217             final int first = 117 + kb + (kx == 3 ? 0 : 4) + 8 * abDef;
218             final int[] aSpecifier = abDef == 1 ? EMPTY_INT_ARRAY : getSpecifier(aCodec, defaultForBand);
219             final int[] bSpecifier = abDef == 2 ? EMPTY_INT_ARRAY : getSpecifier(bCodec, defaultForBand);
220             final int[] specifier = new int[1 + (kx == 3 ? 0 : 1) + aSpecifier.length + bSpecifier.length];
221             specifier[0] = first;
222             int index = 1;
223             if (kx != 3) {
224                 specifier[1] = kx;
225                 index++;
226             }
227             for (final int element : aSpecifier) {
228                 specifier[index] = element;
229                 index++;
230             }
231             for (final int element : bSpecifier) {
232                 specifier[index] = element;
233                 index++;
234             }
235             return specifier;
236         }
237         if (codec instanceof PopulationCodec) {
238             final PopulationCodec populationCodec = (PopulationCodec) codec;
239             final Codec tokenCodec = populationCodec.getTokenCodec();
240             final Codec favouredCodec = populationCodec.getFavouredCodec();
241             final Codec unfavouredCodec = populationCodec.getUnfavouredCodec();
242             final int fDef = favouredCodec.equals(defaultForBand) ? 1 : 0;
243             final int uDef = unfavouredCodec.equals(defaultForBand) ? 1 : 0;
244             int tDefL = 0;
245             final int[] favoured = populationCodec.getFavoured();
246             if (favoured != null) {
247                 if (tokenCodec == Codec.BYTE1) {
248                     tDefL = 1;
249                 } else if (tokenCodec instanceof BHSDCodec) {
250                     final BHSDCodec tokenBHSD = (BHSDCodec) tokenCodec;
251                     if (tokenBHSD.getS() == 0) {
252                         final int[] possibleLValues = { 4, 8, 16, 32, 64, 128, 192, 224, 240, 248, 252 };
253                         final int l = 256 - tokenBHSD.getH();
254                         int index = Arrays.binarySearch(possibleLValues, l);
255                         if (index != -1) {
256                             // TODO: check range is ok for ks
257                             tDefL = index++;
258                         }
259                     }
260                 }
261             }
262             final int first = 141 + fDef + 2 * uDef + 4 * tDefL;
263             final int[] favouredSpecifier = fDef == 1 ? EMPTY_INT_ARRAY : getSpecifier(favouredCodec, defaultForBand);
264             final int[] tokenSpecifier = tDefL != 0 ? EMPTY_INT_ARRAY : getSpecifier(tokenCodec, defaultForBand);
265             final int[] unfavouredSpecifier = uDef == 1 ? EMPTY_INT_ARRAY : getSpecifier(unfavouredCodec, defaultForBand);
266             final int[] specifier = new int[1 + favouredSpecifier.length + unfavouredSpecifier.length + tokenSpecifier.length];
267             specifier[0] = first;
268             int index = 1;
269             for (final int element : favouredSpecifier) {
270                 specifier[index] = element;
271                 index++;
272             }
273             for (final int element : tokenSpecifier) {
274                 specifier[index] = element;
275                 index++;
276             }
277             for (final int element : unfavouredSpecifier) {
278                 specifier[index] = element;
279                 index++;
280             }
281             return specifier;
282         }
283 
284         return null;
285     }
286 
287     public static int getSpecifierForDefaultCodec(final BHSDCodec defaultCodec) {
288         return getSpecifier(defaultCodec, null)[0];
289     }
290 }