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