blob: c816171fa932ff8dec89452e08e29823d7dbd870 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package com.sun.java.util.jar.pack;
27
28import java.util.*;
29import java.io.*;
30
31/**
32 * Adaptive coding.
33 * See the section "Adaptive Encodings" in the Pack200 spec.
34 * @author John Rose
35 */
36class AdaptiveCoding implements Constants, CodingMethod {
37 CodingMethod headCoding;
38 int headLength;
39 CodingMethod tailCoding;
40
41 public AdaptiveCoding(int headLength, CodingMethod headCoding, CodingMethod tailCoding) {
42 assert(isCodableLength(headLength));
43 this.headLength = headLength;
44 this.headCoding = headCoding;
45 this.tailCoding = tailCoding;
46 }
47
48 public void setHeadCoding(CodingMethod headCoding) {
49 this.headCoding = headCoding;
50 }
51 public void setHeadLength(int headLength) {
52 assert(isCodableLength(headLength));
53 this.headLength = headLength;
54 }
55 public void setTailCoding(CodingMethod tailCoding) {
56 this.tailCoding = tailCoding;
57 }
58
59 public boolean isTrivial() {
60 return headCoding == tailCoding;
61 }
62
63 // CodingMethod methods.
64 public void writeArrayTo(OutputStream out, int[] a, int start, int end) throws IOException {
65 writeArray(this, out, a, start, end);
66 }
67 // writeArrayTo must be coded iteratively, not recursively:
68 private static void writeArray(AdaptiveCoding run, OutputStream out, int[] a, int start, int end) throws IOException {
69 for (;;) {
70 int mid = start+run.headLength;
71 assert(mid <= end);
72 run.headCoding.writeArrayTo(out, a, start, mid);
73 start = mid;
74 if (run.tailCoding instanceof AdaptiveCoding) {
75 run = (AdaptiveCoding) run.tailCoding;
76 continue;
77 }
78 break;
79 }
80 run.tailCoding.writeArrayTo(out, a, start, end);
81 }
82
83 public void readArrayFrom(InputStream in, int[] a, int start, int end) throws IOException {
84 readArray(this, in, a, start, end);
85 }
86 private static void readArray(AdaptiveCoding run, InputStream in, int[] a, int start, int end) throws IOException {
87 for (;;) {
88 int mid = start+run.headLength;
89 assert(mid <= end);
90 run.headCoding.readArrayFrom(in, a, start, mid);
91 start = mid;
92 if (run.tailCoding instanceof AdaptiveCoding) {
93 run = (AdaptiveCoding) run.tailCoding;
94 continue;
95 }
96 break;
97 }
98 run.tailCoding.readArrayFrom(in, a, start, end);
99 }
100
101 public static final int KX_MIN = 0;
102 public static final int KX_MAX = 3;
103 public static final int KX_LG2BASE = 4;
104 public static final int KX_BASE = 16;
105
106 public static final int KB_MIN = 0x00;
107 public static final int KB_MAX = 0xFF;
108 public static final int KB_OFFSET = 1;
109 public static final int KB_DEFAULT = 3;
110
111 static int getKXOf(int K) {
112 for (int KX = KX_MIN; KX <= KX_MAX; KX++) {
113 if (((K - KB_OFFSET) & ~KB_MAX) == 0)
114 return KX;
115 K >>>= KX_LG2BASE;
116 }
117 return -1;
118 }
119
120 static int getKBOf(int K) {
121 int KX = getKXOf(K);
122 if (KX < 0) return -1;
123 K >>>= (KX * KX_LG2BASE);
124 return K-1;
125 }
126
127 static int decodeK(int KX, int KB) {
128 assert(KX_MIN <= KX && KX <= KX_MAX);
129 assert(KB_MIN <= KB && KB <= KB_MAX);
130 return (KB+KB_OFFSET) << (KX * KX_LG2BASE);
131 }
132
133 static int getNextK(int K) {
134 if (K <= 0) return 1; // 1st K value
135 int KX = getKXOf(K);
136 if (KX < 0) return Integer.MAX_VALUE;
137 // This is the increment we expect to apply:
138 int unit = 1 << (KX * KX_LG2BASE);
139 int mask = KB_MAX << (KX * KX_LG2BASE);
140 int K1 = K + unit;
141 K1 &= ~(unit-1); // cut off stray low-order bits
142 if (((K1 - unit) & ~mask) == 0) {
143 assert(getKXOf(K1) == KX);
144 return K1;
145 }
146 if (KX == KX_MAX) return Integer.MAX_VALUE;
147 KX += 1;
148 int unit2 = 1 << (KX * KX_LG2BASE);
149 int mask2 = KB_MAX << (KX * KX_LG2BASE);
150 K1 |= (mask & ~mask2);
151 K1 += unit;
152 assert(getKXOf(K1) == KX);
153 return K1;
154 }
155
156 // Is K of the form ((KB:[0..255])+1) * 16^(KX:{0..3])?
157 public static boolean isCodableLength(int K) {
158 int KX = getKXOf(K);
159 if (KX < 0) return false;
160 int unit = 1 << (KX * KX_LG2BASE);
161 int mask = KB_MAX << (KX * KX_LG2BASE);
162 return ((K - unit) & ~mask) == 0;
163 }
164
165 public byte[] getMetaCoding(Coding dflt) {
166 //assert(!isTrivial()); // can happen
167 // See the isCodableLength restriction in CodingChooser.
168 ByteArrayOutputStream bytes = new ByteArrayOutputStream(10);
169 try {
170 makeMetaCoding(this, dflt, bytes);
171 } catch (IOException ee) {
172 throw new RuntimeException(ee);
173 }
174 return bytes.toByteArray();
175 }
176 private static void makeMetaCoding(AdaptiveCoding run, Coding dflt,
177 ByteArrayOutputStream bytes)
178 throws IOException {
179 for (;;) {
180 CodingMethod headCoding = run.headCoding;
181 int headLength = run.headLength;
182 CodingMethod tailCoding = run.tailCoding;
183 int K = headLength;
184 assert(isCodableLength(K));
185 int ADef = (headCoding == dflt)?1:0;
186 int BDef = (tailCoding == dflt)?1:0;
187 if (ADef+BDef > 1) BDef = 0; // arbitrary choice
188 int ABDef = 1*ADef + 2*BDef;
189 assert(ABDef < 3);
190 int KX = getKXOf(K);
191 int KB = getKBOf(K);
192 assert(decodeK(KX, KB) == K);
193 int KBFlag = (KB != KB_DEFAULT)?1:0;
194 bytes.write(_meta_run + KX + 4*KBFlag + 8*ABDef);
195 if (KBFlag != 0) bytes.write(KB);
196 if (ADef == 0) bytes.write(headCoding.getMetaCoding(dflt));
197 if (tailCoding instanceof AdaptiveCoding) {
198 run = (AdaptiveCoding) tailCoding;
199 continue; // tail call, to avoid deep stack recursion
200 }
201 if (BDef == 0) bytes.write(tailCoding.getMetaCoding(dflt));
202 break;
203 }
204 }
205 public static int parseMetaCoding(byte[] bytes, int pos, Coding dflt, CodingMethod res[]) {
206 int op = bytes[pos++] & 0xFF;
207 if (op < _meta_run || op >= _meta_pop) return pos-1; // backup
208 AdaptiveCoding prevc = null;
209 for (boolean keepGoing = true; keepGoing; ) {
210 keepGoing = false;
211 assert(op >= _meta_run);
212 op -= _meta_run;
213 int KX = op % 4;
214 int KBFlag = (op / 4) % 2;
215 int ABDef = (op / 8);
216 assert(ABDef < 3);
217 int ADef = (ABDef & 1);
218 int BDef = (ABDef & 2);
219 CodingMethod[] ACode = {dflt}, BCode = {dflt};
220 int KB = KB_DEFAULT;
221 if (KBFlag != 0)
222 KB = bytes[pos++] & 0xFF;
223 if (ADef == 0) {
224 pos = BandStructure.parseMetaCoding(bytes, pos, dflt, ACode);
225 }
226 if (BDef == 0 &&
227 ((op = bytes[pos] & 0xFF) >= _meta_run) && op < _meta_pop) {
228 pos++;
229 keepGoing = true;
230 } else if (BDef == 0) {
231 pos = BandStructure.parseMetaCoding(bytes, pos, dflt, BCode);
232 }
233 AdaptiveCoding newc = new AdaptiveCoding(decodeK(KX, KB),
234 ACode[0], BCode[0]);
235 if (prevc == null) {
236 res[0] = newc;
237 } else {
238 prevc.tailCoding = newc;
239 }
240 prevc = newc;
241 }
242 return pos;
243 }
244
245 private String keyString(CodingMethod m) {
246 if (m instanceof Coding)
247 return ((Coding)m).keyString();
248 return m.toString();
249 }
250 public String toString() {
251 StringBuffer res = new StringBuffer(20);
252 AdaptiveCoding run = this;
253 res.append("run(");
254 for (;;) {
255 res.append(run.headLength).append("*");
256 res.append(keyString(run.headCoding));
257 if (run.tailCoding instanceof AdaptiveCoding) {
258 run = (AdaptiveCoding) run.tailCoding;
259 res.append(" ");
260 continue;
261 }
262 break;
263 }
264 res.append(" **").append(keyString(run.tailCoding));
265 res.append(")");
266 return res.toString();
267 }
268
269/*
270 public static void main(String av[]) {
271 int[][] samples = {
272 {1,2,3,4,5},
273 {254,255,256,256+1*16,256+2*16},
274 {0xfd,0xfe,0xff,0x100,0x110,0x120,0x130},
275 {0xfd0,0xfe0,0xff0,0x1000,0x1100,0x1200,0x1300},
276 {0xfd00,0xfe00,0xff00,0x10000,0x11000,0x12000,0x13000},
277 {0xfd000,0xfe000,0xff000,0x100000}
278 };
279 for (int i = 0; i < samples.length; i++) {
280 for (int j = 0; j < samples[i].length; j++) {
281 int K = samples[i][j];
282 int KX = getKXOf(K);
283 int KB = getKBOf(K);
284 System.out.println("K="+Integer.toHexString(K)+
285 " KX="+KX+" KB="+KB);
286 assert(isCodableLength(K));
287 assert(K == decodeK(KX, KB));
288 if (j == 0) continue;
289 int K1 = samples[i][j-1];
290 assert(K == getNextK(K1));
291 }
292 }
293 }
294//*/
295
296}