0N/A/*
3261N/A * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage com.sun.java.util.jar.pack;
0N/A
3063N/Aimport java.io.ByteArrayOutputStream;
3063N/Aimport java.io.IOException;
3063N/Aimport java.io.InputStream;
3063N/Aimport java.io.OutputStream;
3315N/Aimport static com.sun.java.util.jar.pack.Constants.*;
0N/A
0N/A/**
0N/A * Adaptive coding.
0N/A * See the section "Adaptive Encodings" in the Pack200 spec.
0N/A * @author John Rose
0N/A */
3315N/Aclass AdaptiveCoding implements CodingMethod {
0N/A CodingMethod headCoding;
0N/A int headLength;
0N/A CodingMethod tailCoding;
0N/A
0N/A public AdaptiveCoding(int headLength, CodingMethod headCoding, CodingMethod tailCoding) {
0N/A assert(isCodableLength(headLength));
0N/A this.headLength = headLength;
0N/A this.headCoding = headCoding;
0N/A this.tailCoding = tailCoding;
0N/A }
0N/A
0N/A public void setHeadCoding(CodingMethod headCoding) {
0N/A this.headCoding = headCoding;
0N/A }
0N/A public void setHeadLength(int headLength) {
0N/A assert(isCodableLength(headLength));
0N/A this.headLength = headLength;
0N/A }
0N/A public void setTailCoding(CodingMethod tailCoding) {
0N/A this.tailCoding = tailCoding;
0N/A }
0N/A
0N/A public boolean isTrivial() {
0N/A return headCoding == tailCoding;
0N/A }
0N/A
0N/A // CodingMethod methods.
0N/A public void writeArrayTo(OutputStream out, int[] a, int start, int end) throws IOException {
0N/A writeArray(this, out, a, start, end);
0N/A }
0N/A // writeArrayTo must be coded iteratively, not recursively:
0N/A private static void writeArray(AdaptiveCoding run, OutputStream out, int[] a, int start, int end) throws IOException {
0N/A for (;;) {
0N/A int mid = start+run.headLength;
0N/A assert(mid <= end);
0N/A run.headCoding.writeArrayTo(out, a, start, mid);
0N/A start = mid;
0N/A if (run.tailCoding instanceof AdaptiveCoding) {
0N/A run = (AdaptiveCoding) run.tailCoding;
0N/A continue;
0N/A }
0N/A break;
0N/A }
0N/A run.tailCoding.writeArrayTo(out, a, start, end);
0N/A }
0N/A
0N/A public void readArrayFrom(InputStream in, int[] a, int start, int end) throws IOException {
0N/A readArray(this, in, a, start, end);
0N/A }
0N/A private static void readArray(AdaptiveCoding run, InputStream in, int[] a, int start, int end) throws IOException {
0N/A for (;;) {
0N/A int mid = start+run.headLength;
0N/A assert(mid <= end);
0N/A run.headCoding.readArrayFrom(in, a, start, mid);
0N/A start = mid;
0N/A if (run.tailCoding instanceof AdaptiveCoding) {
0N/A run = (AdaptiveCoding) run.tailCoding;
0N/A continue;
0N/A }
0N/A break;
0N/A }
0N/A run.tailCoding.readArrayFrom(in, a, start, end);
0N/A }
0N/A
0N/A public static final int KX_MIN = 0;
0N/A public static final int KX_MAX = 3;
0N/A public static final int KX_LG2BASE = 4;
0N/A public static final int KX_BASE = 16;
0N/A
0N/A public static final int KB_MIN = 0x00;
0N/A public static final int KB_MAX = 0xFF;
0N/A public static final int KB_OFFSET = 1;
0N/A public static final int KB_DEFAULT = 3;
0N/A
0N/A static int getKXOf(int K) {
0N/A for (int KX = KX_MIN; KX <= KX_MAX; KX++) {
0N/A if (((K - KB_OFFSET) & ~KB_MAX) == 0)
0N/A return KX;
0N/A K >>>= KX_LG2BASE;
0N/A }
0N/A return -1;
0N/A }
0N/A
0N/A static int getKBOf(int K) {
0N/A int KX = getKXOf(K);
0N/A if (KX < 0) return -1;
0N/A K >>>= (KX * KX_LG2BASE);
0N/A return K-1;
0N/A }
0N/A
0N/A static int decodeK(int KX, int KB) {
0N/A assert(KX_MIN <= KX && KX <= KX_MAX);
0N/A assert(KB_MIN <= KB && KB <= KB_MAX);
0N/A return (KB+KB_OFFSET) << (KX * KX_LG2BASE);
0N/A }
0N/A
0N/A static int getNextK(int K) {
0N/A if (K <= 0) return 1; // 1st K value
0N/A int KX = getKXOf(K);
0N/A if (KX < 0) return Integer.MAX_VALUE;
0N/A // This is the increment we expect to apply:
0N/A int unit = 1 << (KX * KX_LG2BASE);
0N/A int mask = KB_MAX << (KX * KX_LG2BASE);
0N/A int K1 = K + unit;
0N/A K1 &= ~(unit-1); // cut off stray low-order bits
0N/A if (((K1 - unit) & ~mask) == 0) {
0N/A assert(getKXOf(K1) == KX);
0N/A return K1;
0N/A }
0N/A if (KX == KX_MAX) return Integer.MAX_VALUE;
0N/A KX += 1;
0N/A int mask2 = KB_MAX << (KX * KX_LG2BASE);
0N/A K1 |= (mask & ~mask2);
0N/A K1 += unit;
0N/A assert(getKXOf(K1) == KX);
0N/A return K1;
0N/A }
0N/A
0N/A // Is K of the form ((KB:[0..255])+1) * 16^(KX:{0..3])?
0N/A public static boolean isCodableLength(int K) {
0N/A int KX = getKXOf(K);
0N/A if (KX < 0) return false;
0N/A int unit = 1 << (KX * KX_LG2BASE);
0N/A int mask = KB_MAX << (KX * KX_LG2BASE);
0N/A return ((K - unit) & ~mask) == 0;
0N/A }
0N/A
0N/A public byte[] getMetaCoding(Coding dflt) {
0N/A //assert(!isTrivial()); // can happen
0N/A // See the isCodableLength restriction in CodingChooser.
0N/A ByteArrayOutputStream bytes = new ByteArrayOutputStream(10);
0N/A try {
0N/A makeMetaCoding(this, dflt, bytes);
0N/A } catch (IOException ee) {
0N/A throw new RuntimeException(ee);
0N/A }
0N/A return bytes.toByteArray();
0N/A }
0N/A private static void makeMetaCoding(AdaptiveCoding run, Coding dflt,
0N/A ByteArrayOutputStream bytes)
0N/A throws IOException {
0N/A for (;;) {
0N/A CodingMethod headCoding = run.headCoding;
0N/A int headLength = run.headLength;
0N/A CodingMethod tailCoding = run.tailCoding;
0N/A int K = headLength;
0N/A assert(isCodableLength(K));
0N/A int ADef = (headCoding == dflt)?1:0;
0N/A int BDef = (tailCoding == dflt)?1:0;
0N/A if (ADef+BDef > 1) BDef = 0; // arbitrary choice
0N/A int ABDef = 1*ADef + 2*BDef;
0N/A assert(ABDef < 3);
0N/A int KX = getKXOf(K);
0N/A int KB = getKBOf(K);
0N/A assert(decodeK(KX, KB) == K);
0N/A int KBFlag = (KB != KB_DEFAULT)?1:0;
0N/A bytes.write(_meta_run + KX + 4*KBFlag + 8*ABDef);
0N/A if (KBFlag != 0) bytes.write(KB);
0N/A if (ADef == 0) bytes.write(headCoding.getMetaCoding(dflt));
0N/A if (tailCoding instanceof AdaptiveCoding) {
0N/A run = (AdaptiveCoding) tailCoding;
0N/A continue; // tail call, to avoid deep stack recursion
0N/A }
0N/A if (BDef == 0) bytes.write(tailCoding.getMetaCoding(dflt));
0N/A break;
0N/A }
0N/A }
0N/A public static int parseMetaCoding(byte[] bytes, int pos, Coding dflt, CodingMethod res[]) {
0N/A int op = bytes[pos++] & 0xFF;
0N/A if (op < _meta_run || op >= _meta_pop) return pos-1; // backup
0N/A AdaptiveCoding prevc = null;
0N/A for (boolean keepGoing = true; keepGoing; ) {
0N/A keepGoing = false;
0N/A assert(op >= _meta_run);
0N/A op -= _meta_run;
0N/A int KX = op % 4;
0N/A int KBFlag = (op / 4) % 2;
0N/A int ABDef = (op / 8);
0N/A assert(ABDef < 3);
0N/A int ADef = (ABDef & 1);
0N/A int BDef = (ABDef & 2);
0N/A CodingMethod[] ACode = {dflt}, BCode = {dflt};
0N/A int KB = KB_DEFAULT;
0N/A if (KBFlag != 0)
0N/A KB = bytes[pos++] & 0xFF;
0N/A if (ADef == 0) {
0N/A pos = BandStructure.parseMetaCoding(bytes, pos, dflt, ACode);
0N/A }
0N/A if (BDef == 0 &&
0N/A ((op = bytes[pos] & 0xFF) >= _meta_run) && op < _meta_pop) {
0N/A pos++;
0N/A keepGoing = true;
0N/A } else if (BDef == 0) {
0N/A pos = BandStructure.parseMetaCoding(bytes, pos, dflt, BCode);
0N/A }
0N/A AdaptiveCoding newc = new AdaptiveCoding(decodeK(KX, KB),
0N/A ACode[0], BCode[0]);
0N/A if (prevc == null) {
0N/A res[0] = newc;
0N/A } else {
0N/A prevc.tailCoding = newc;
0N/A }
0N/A prevc = newc;
0N/A }
0N/A return pos;
0N/A }
0N/A
0N/A private String keyString(CodingMethod m) {
0N/A if (m instanceof Coding)
0N/A return ((Coding)m).keyString();
0N/A return m.toString();
0N/A }
0N/A public String toString() {
3315N/A StringBuilder res = new StringBuilder(20);
0N/A AdaptiveCoding run = this;
0N/A res.append("run(");
0N/A for (;;) {
0N/A res.append(run.headLength).append("*");
0N/A res.append(keyString(run.headCoding));
0N/A if (run.tailCoding instanceof AdaptiveCoding) {
0N/A run = (AdaptiveCoding) run.tailCoding;
0N/A res.append(" ");
0N/A continue;
0N/A }
0N/A break;
0N/A }
0N/A res.append(" **").append(keyString(run.tailCoding));
0N/A res.append(")");
0N/A return res.toString();
0N/A }
0N/A
0N/A/*
0N/A public static void main(String av[]) {
0N/A int[][] samples = {
0N/A {1,2,3,4,5},
0N/A {254,255,256,256+1*16,256+2*16},
0N/A {0xfd,0xfe,0xff,0x100,0x110,0x120,0x130},
0N/A {0xfd0,0xfe0,0xff0,0x1000,0x1100,0x1200,0x1300},
0N/A {0xfd00,0xfe00,0xff00,0x10000,0x11000,0x12000,0x13000},
0N/A {0xfd000,0xfe000,0xff000,0x100000}
0N/A };
0N/A for (int i = 0; i < samples.length; i++) {
0N/A for (int j = 0; j < samples[i].length; j++) {
0N/A int K = samples[i][j];
0N/A int KX = getKXOf(K);
0N/A int KB = getKBOf(K);
0N/A System.out.println("K="+Integer.toHexString(K)+
0N/A " KX="+KX+" KB="+KB);
0N/A assert(isCodableLength(K));
0N/A assert(K == decodeK(KX, KB));
0N/A if (j == 0) continue;
0N/A int K1 = samples[i][j-1];
0N/A assert(K == getNextK(K1));
0N/A }
0N/A }
0N/A }
0N/A//*/
0N/A
0N/A}