|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
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.*; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
|
|
|
|
public void writeArrayTo(OutputStream out, int[] a, int start, int end) throws IOException { |
|
writeArray(this, out, a, start, end); |
|
} |
|
|
|
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; |
|
int KX = getKXOf(K); |
|
if (KX < 0) return Integer.MAX_VALUE; |
|
|
|
int unit = 1 << (KX * KX_LG2BASE); |
|
int mask = KB_MAX << (KX * KX_LG2BASE); |
|
int K1 = K + unit; |
|
K1 &= ~(unit-1); |
|
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; |
|
} |
|
|
|
|
|
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 |
|
|
|
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; |
|
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; |
|
} |
|
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; |
|
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)); |
|
} |
|
} |
|
} |
|
//*/ |
|
|
|
} |