298 lines · 10.6 KB · Java
RawDownload
/* * 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));            }        }    }//*/ }