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