|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package com.sun.java.util.jar.pack; |
|
|
|
import java.io.IOException; |
|
import java.io.InputStream; |
|
import java.io.OutputStream; |
|
import java.util.HashMap; |
|
import java.util.Map; |
|
import static com.sun.java.util.jar.pack.Constants.*; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
class Coding implements Comparable<Coding>, CodingMethod, Histogram.BitMetric { |
|
/* |
|
Coding schema for single integers, parameterized by (B,H,S): |
|
|
|
Let B in [1,5], H in [1,256], S in [0,3]. |
|
(S limit is arbitrary. B follows the 32-bit limit. H is byte size.) |
|
|
|
A given (B,H,S) code varies in length from 1 to B bytes. |
|
|
|
The 256 values a byte may take on are divided into L=(256-H) and H |
|
values, with all the H values larger than the L values. |
|
(That is, the L values are [0,L) and the H are [L,256).) |
|
|
|
The last byte is always either the B-th byte, a byte with "L value" |
|
(<L), or both. There is no other byte that satisfies these conditions. |
|
All bytes before the last always have "H values" (>=L). |
|
|
|
Therefore, if L==0, the code always has the full length of B bytes. |
|
The coding then becomes a classic B-byte little-endian unsigned integer. |
|
(Also, if L==128, the high bit of each byte acts signals the presence |
|
of a following byte, up to the maximum length.) |
|
|
|
In the unsigned case (S==0), the coding is compact and monotonic |
|
in the ordering of byte sequences defined by appending zero bytes |
|
to pad them to a common length B, reversing them, and ordering them |
|
lexicographically. (This agrees with "little-endian" byte order.) |
|
|
|
Therefore, the unsigned value of a byte sequence may be defined as: |
|
<pre> |
|
U(b0) == b0 |
|
in [0..L) |
|
or [0..256) if B==1 (**) |
|
|
|
U(b0,b1) == b0 + b1*H |
|
in [L..L*(1+H)) |
|
or [L..L*(1+H) + H^2) if B==2 |
|
|
|
U(b0,b1,b2) == b0 + b1*H + b2*H^2 |
|
in [L*(1+H)..L*(1+H+H^2)) |
|
or [L*(1+H)..L*(1+H+H^2) + H^3) if B==3 |
|
|
|
U(b[i]: i<n) == Sum[i<n]( b[i] * H^i ) |
|
up to L*Sum[i<n]( H^i ) |
|
or to L*Sum[i<n]( H^i ) + H^n if n==B |
|
</pre> |
|
|
|
(**) If B==1, the values H,L play no role in the coding. |
|
As a convention, we require that any (1,H,S) code must always |
|
encode values less than H. Thus, a simple unsigned byte is coded |
|
specifically by the code (1,256,0). |
|
|
|
(Properly speaking, the unsigned case should be parameterized as |
|
S==Infinity. If the schema were regular, the case S==0 would really |
|
denote a numbering in which all coded values are negative.) |
|
|
|
If S>0, the unsigned value of a byte sequence is regarded as a binary |
|
integer. If any of the S low-order bits are zero, the corresponding |
|
signed value will be non-negative. If all of the S low-order bits |
|
(S>0) are one, the the corresponding signed value will be negative. |
|
|
|
The non-negative signed values are compact and monotonically increasing |
|
(from 0) in the ordering of the corresponding unsigned values. |
|
|
|
The negative signed values are compact and monotonically decreasing |
|
(from -1) in the ordering of the corresponding unsigned values. |
|
|
|
In essence, the low-order S bits function as a collective sign bit |
|
for negative signed numbers, and as a low-order base-(2^S-1) digit |
|
for non-negative signed numbers. |
|
|
|
Therefore, the signed value corresponding to an unsigned value is: |
|
<pre> |
|
Sgn(x) == x if S==0 |
|
Sgn(x) == (x / 2^S)*(2^S-1) + (x % 2^S), if S>0, (x % 2^S) < 2^S-1 |
|
Sgn(x) == -(x / 2^S)-1, if S>0, (x % 2^S) == 2^S-1 |
|
</pre> |
|
|
|
Finally, the value of a byte sequence, given the coding parameters |
|
(B,H,S), is defined as: |
|
<pre> |
|
V(b[i]: i<n) == Sgn(U(b[i]: i<n)) |
|
</pre> |
|
|
|
The extremal positive and negative signed value for a given range |
|
of unsigned values may be found by sign-encoding the largest unsigned |
|
value which is not 2^S-1 mod 2^S, and that which is, respectively. |
|
|
|
Because B,H,S are variable, this is not a single coding but a schema |
|
of codings. For optimal compression, it is necessary to adaptively |
|
select specific codings to the data being compressed. |
|
|
|
For example, if a sequence of values happens never to be negative, |
|
S==0 is the best choice. If the values are equally balanced between |
|
negative and positive, S==1. If negative values are rare, then S>1 |
|
is more appropriate. |
|
|
|
A (B,H,S) encoding is called a "subrange" if it does not encode |
|
the largest 32-bit value, and if the number R of values it does |
|
encode can be expressed as a positive 32-bit value. (Note that |
|
B=1 implies R<=256, B=2 implies R<=65536, etc.) |
|
|
|
A delta version of a given (B,H,S) coding encodes an array of integers |
|
by writing their successive differences in the (B,H,S) coding. |
|
The original integers themselves may be recovered by making a |
|
running accumulation of sum of the differences as they are read. |
|
|
|
As a special case, if a (B,H,S) encoding is a subrange, its delta |
|
version will only encode arrays of numbers in the coding's unsigned |
|
range, [0..R-1]. The coding of deltas is still in the normal signed |
|
range, if S!=0. During delta encoding, all subtraction results are |
|
reduced to the signed range, by adding multiples of R. Likewise, |
|
. during encoding, all addition results are reduced to the unsigned range. |
|
This special case for subranges allows the benefits of wraparound |
|
when encoding correlated sequences of very small positive numbers. |
|
*/ |
|
|
|
|
|
private static int saturate32(long x) { |
|
if (x > Integer.MAX_VALUE) return Integer.MAX_VALUE; |
|
if (x < Integer.MIN_VALUE) return Integer.MIN_VALUE; |
|
return (int)x; |
|
} |
|
private static long codeRangeLong(int B, int H) { |
|
return codeRangeLong(B, H, B); |
|
} |
|
private static long codeRangeLong(int B, int H, int nMax) { |
|
// Code range for a all (B,H) codes of length <=nMax (<=B). |
|
// n < B: L*Sum[i<n]( H^i ) |
|
|
|
assert(nMax >= 0 && nMax <= B); |
|
assert(B >= 1 && B <= 5); |
|
assert(H >= 1 && H <= 256); |
|
if (nMax == 0) return 0; |
|
if (B == 1) return H; |
|
int L = 256-H; |
|
long sum = 0; |
|
long H_i = 1; |
|
for (int n = 1; n <= nMax; n++) { |
|
sum += H_i; |
|
H_i *= H; |
|
} |
|
sum *= L; |
|
if (nMax == B) |
|
sum += H_i; |
|
return sum; |
|
} |
|
|
|
public static int codeMax(int B, int H, int S, int nMax) { |
|
|
|
long range = codeRangeLong(B, H, nMax); |
|
if (range == 0) |
|
return -1; |
|
if (S == 0 || range >= (long)1<<32) |
|
return saturate32(range-1); |
|
long maxPos = range-1; |
|
while (isNegativeCode(maxPos, S)) { |
|
--maxPos; |
|
} |
|
if (maxPos < 0) return -1; |
|
int smax = decodeSign32(maxPos, S); |
|
|
|
if (smax < 0) |
|
return Integer.MAX_VALUE; |
|
return smax; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public static int codeMin(int B, int H, int S, int nMax) { |
|
|
|
long range = codeRangeLong(B, H, nMax); |
|
if (range >= (long)1<<32 && nMax == B) { |
|
|
|
return Integer.MIN_VALUE; |
|
} |
|
if (S == 0) { |
|
return 0; |
|
} |
|
long maxNeg = range-1; |
|
while (!isNegativeCode(maxNeg, S)) |
|
--maxNeg; |
|
|
|
if (maxNeg < 0) return 0; |
|
return decodeSign32(maxNeg, S); |
|
} |
|
|
|
// Some of the arithmetic below is on unsigned 32-bit integers. |
|
// These must be represented in Java as longs in the range [0..2^32-1]. |
|
// The conversion to a signed int is just the Java cast (int), but |
|
|
|
private static long toUnsigned32(int sx) { |
|
return ((long)sx << 32) >>> 32; |
|
} |
|
|
|
|
|
private static boolean isNegativeCode(long ux, int S) { |
|
assert(S > 0); |
|
assert(ux >= -1); |
|
int Smask = (1<<S)-1; |
|
return (((int)ux+1) & Smask) == 0; |
|
} |
|
private static boolean hasNegativeCode(int sx, int S) { |
|
assert(S > 0); |
|
// If S>=2 very low negatives are coded by 32-bit-wrapped positives. |
|
// The lowest negative representable by a negative coding is |
|
// ~(umax32 >> S), and the next lower number is coded by wrapping |
|
// the highest positive: |
|
// CodePos(umax32-1) -> (umax32-1)-((umax32-1)>>S) |
|
|
|
return (0 > sx) && (sx >= ~(-1>>>S)); |
|
} |
|
private static int decodeSign32(long ux, int S) { |
|
assert(ux == toUnsigned32((int)ux)) |
|
: (Long.toHexString(ux)); |
|
if (S == 0) { |
|
return (int) ux; |
|
} |
|
int sx; |
|
if (isNegativeCode(ux, S)) { |
|
|
|
sx = ~((int)ux >>> S); |
|
} else { |
|
|
|
sx = (int)ux - ((int)ux >>> S); |
|
} |
|
|
|
assert(!(S == 1) || sx == (((int)ux >>> 1) ^ -((int)ux & 1))); |
|
return sx; |
|
} |
|
private static long encodeSign32(int sx, int S) { |
|
if (S == 0) { |
|
return toUnsigned32(sx); |
|
} |
|
int Smask = (1<<S)-1; |
|
long ux; |
|
if (!hasNegativeCode(sx, S)) { |
|
|
|
ux = sx + (toUnsigned32(sx) / Smask); |
|
} else { |
|
|
|
ux = (-sx << S) - 1; |
|
} |
|
ux = toUnsigned32((int)ux); |
|
assert(sx == decodeSign32(ux, S)) |
|
: (Long.toHexString(ux)+" -> "+ |
|
Integer.toHexString(sx)+" != "+ |
|
Integer.toHexString(decodeSign32(ux, S))); |
|
return ux; |
|
} |
|
|
|
|
|
public static void writeInt(byte[] out, int[] outpos, int sx, int B, int H, int S) { |
|
long ux = encodeSign32(sx, S); |
|
assert(ux == toUnsigned32((int)ux)); |
|
assert(ux < codeRangeLong(B, H)) |
|
: Long.toHexString(ux); |
|
int L = 256-H; |
|
long sum = ux; |
|
int pos = outpos[0]; |
|
for (int i = 0; i < B-1; i++) { |
|
if (sum < L) |
|
break; |
|
sum -= L; |
|
int b_i = (int)( L + (sum % H) ); |
|
sum /= H; |
|
out[pos++] = (byte)b_i; |
|
} |
|
out[pos++] = (byte)sum; |
|
|
|
outpos[0] = pos; |
|
// Check right away for mis-coding. |
|
//assert(sx == readInt(out, new int[1], B, H, S)); |
|
} |
|
public static int readInt(byte[] in, int[] inpos, int B, int H, int S) { |
|
|
|
int L = 256-H; |
|
long sum = 0; |
|
long H_i = 1; |
|
int pos = inpos[0]; |
|
for (int i = 0; i < B; i++) { |
|
int b_i = in[pos++] & 0xFF; |
|
sum += b_i*H_i; |
|
H_i *= H; |
|
if (b_i < L) break; |
|
} |
|
//assert(sum >= 0 && sum < codeRangeLong(B, H)); |
|
|
|
inpos[0] = pos; |
|
return decodeSign32(sum, S); |
|
} |
|
|
|
public static int readIntFrom(InputStream in, int B, int H, int S) throws IOException { |
|
|
|
int L = 256-H; |
|
long sum = 0; |
|
long H_i = 1; |
|
for (int i = 0; i < B; i++) { |
|
int b_i = in.read(); |
|
if (b_i < 0) throw new RuntimeException("unexpected EOF"); |
|
sum += b_i*H_i; |
|
H_i *= H; |
|
if (b_i < L) break; |
|
} |
|
assert(sum >= 0 && sum < codeRangeLong(B, H)); |
|
return decodeSign32(sum, S); |
|
} |
|
|
|
public static final int B_MAX = 5; |
|
public static final int H_MAX = 256; |
|
public static final int S_MAX = 2; /* S: [0,2] */ |
|
|
|
// END OF STATICS. |
|
|
|
private final int B; /*1..5*/ |
|
private final int H; /*1..256*/ |
|
private final int L; /*0..255*/ |
|
private final int S; /*0..3*/ |
|
private final int del; /*0..2*/ |
|
private final int min; |
|
private final int max; |
|
private final int umin; |
|
private final int umax; |
|
private final int[] byteMin; |
|
private final int[] byteMax; |
|
|
|
private Coding(int B, int H, int S) { |
|
this(B, H, S, 0); |
|
} |
|
private Coding(int B, int H, int S, int del) { |
|
this.B = B; |
|
this.H = H; |
|
this.L = 256-H; |
|
this.S = S; |
|
this.del = del; |
|
this.min = codeMin(B, H, S, B); |
|
this.max = codeMax(B, H, S, B); |
|
this.umin = codeMin(B, H, 0, B); |
|
this.umax = codeMax(B, H, 0, B); |
|
this.byteMin = new int[B]; |
|
this.byteMax = new int[B]; |
|
|
|
for (int nMax = 1; nMax <= B; nMax++) { |
|
byteMin[nMax-1] = codeMin(B, H, S, nMax); |
|
byteMax[nMax-1] = codeMax(B, H, S, nMax); |
|
} |
|
} |
|
|
|
public boolean equals(Object x) { |
|
if (!(x instanceof Coding)) return false; |
|
Coding that = (Coding) x; |
|
if (this.B != that.B) return false; |
|
if (this.H != that.H) return false; |
|
if (this.S != that.S) return false; |
|
if (this.del != that.del) return false; |
|
return true; |
|
} |
|
|
|
public int hashCode() { |
|
return (del<<14)+(S<<11)+(B<<8)+(H<<0); |
|
} |
|
|
|
private static Map<Coding, Coding> codeMap; |
|
|
|
private static synchronized Coding of(int B, int H, int S, int del) { |
|
if (codeMap == null) codeMap = new HashMap<>(); |
|
Coding x0 = new Coding(B, H, S, del); |
|
Coding x1 = codeMap.get(x0); |
|
if (x1 == null) codeMap.put(x0, x1 = x0); |
|
return x1; |
|
} |
|
|
|
public static Coding of(int B, int H) { |
|
return of(B, H, 0, 0); |
|
} |
|
|
|
public static Coding of(int B, int H, int S) { |
|
return of(B, H, S, 0); |
|
} |
|
|
|
public boolean canRepresentValue(int x) { |
|
if (isSubrange()) |
|
return canRepresentUnsigned(x); |
|
else |
|
return canRepresentSigned(x); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean canRepresentSigned(int x) { |
|
return (x >= min && x <= max); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean canRepresentUnsigned(int x) { |
|
return (x >= umin && x <= umax); |
|
} |
|
|
|
|
|
public int readFrom(byte[] in, int[] inpos) { |
|
return readInt(in, inpos, B, H, S); |
|
} |
|
public void writeTo(byte[] out, int[] outpos, int x) { |
|
writeInt(out, outpos, x, B, H, S); |
|
} |
|
|
|
|
|
public int readFrom(InputStream in) throws IOException { |
|
return readIntFrom(in, B, H, S); |
|
} |
|
public void writeTo(OutputStream out, int x) throws IOException { |
|
byte[] buf = new byte[B]; |
|
int[] pos = new int[1]; |
|
writeInt(buf, pos, x, B, H, S); |
|
out.write(buf, 0, pos[0]); |
|
} |
|
|
|
|
|
public void readArrayFrom(InputStream in, int[] a, int start, int end) throws IOException { |
|
|
|
for (int i = start; i < end; i++) |
|
a[i] = readFrom(in); |
|
|
|
for (int dstep = 0; dstep < del; dstep++) { |
|
long state = 0; |
|
for (int i = start; i < end; i++) { |
|
state += a[i]; |
|
|
|
if (isSubrange()) { |
|
state = reduceToUnsignedRange(state); |
|
} |
|
a[i] = (int) state; |
|
} |
|
} |
|
} |
|
public void writeArrayTo(OutputStream out, int[] a, int start, int end) throws IOException { |
|
if (end <= start) return; |
|
for (int dstep = 0; dstep < del; dstep++) { |
|
int[] deltas; |
|
if (!isSubrange()) |
|
deltas = makeDeltas(a, start, end, 0, 0); |
|
else |
|
deltas = makeDeltas(a, start, end, min, max); |
|
a = deltas; |
|
start = 0; |
|
end = deltas.length; |
|
} |
|
// The following code is a buffered version of this loop: |
|
// for (int i = start; i < end; i++) |
|
|
|
byte[] buf = new byte[1<<8]; |
|
final int bufmax = buf.length-B; |
|
int[] pos = { 0 }; |
|
for (int i = start; i < end; ) { |
|
while (pos[0] <= bufmax) { |
|
writeTo(buf, pos, a[i++]); |
|
if (i >= end) break; |
|
} |
|
out.write(buf, 0, pos[0]); |
|
pos[0] = 0; |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
boolean isSubrange() { |
|
return max < Integer.MAX_VALUE |
|
&& ((long)max - (long)min + 1) <= Integer.MAX_VALUE; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
boolean isFullRange() { |
|
return max == Integer.MAX_VALUE && min == Integer.MIN_VALUE; |
|
} |
|
|
|
|
|
int getRange() { |
|
assert(isSubrange()); |
|
return (max - min) + 1; |
|
} |
|
|
|
Coding setB(int B) { return Coding.of(B, H, S, del); } |
|
Coding setH(int H) { return Coding.of(B, H, S, del); } |
|
Coding setS(int S) { return Coding.of(B, H, S, del); } |
|
Coding setL(int L) { return setH(256-L); } |
|
Coding setD(int del) { return Coding.of(B, H, S, del); } |
|
Coding getDeltaCoding() { return setD(del+1); } |
|
|
|
|
|
Coding getValueCoding() { |
|
if (isDelta()) |
|
return Coding.of(B, H, 0, del-1); |
|
else |
|
return this; |
|
} |
|
|
|
|
|
|
|
*/ |
|
int reduceToUnsignedRange(long value) { |
|
if (value == (int)value && canRepresentUnsigned((int)value)) |
|
|
|
return (int)value; |
|
int range = getRange(); |
|
assert(range > 0); |
|
value %= range; |
|
if (value < 0) value += range; |
|
assert(canRepresentUnsigned((int)value)); |
|
return (int)value; |
|
} |
|
|
|
int reduceToSignedRange(int value) { |
|
if (canRepresentSigned(value)) |
|
|
|
return value; |
|
return reduceToSignedRange(value, min, max); |
|
} |
|
static int reduceToSignedRange(int value, int min, int max) { |
|
int range = (max-min+1); |
|
assert(range > 0); |
|
int value0 = value; |
|
value -= min; |
|
if (value < 0 && value0 >= 0) { |
|
|
|
value -= range; |
|
assert(value >= 0); |
|
} |
|
value %= range; |
|
if (value < 0) value += range; |
|
value += min; |
|
assert(min <= value && value <= max); |
|
return value; |
|
} |
|
|
|
|
|
|
|
*/ |
|
boolean isSigned() { |
|
return min < 0; |
|
} |
|
|
|
boolean isDelta() { |
|
return del != 0; |
|
} |
|
|
|
public int B() { return B; } |
|
public int H() { return H; } |
|
public int L() { return L; } |
|
public int S() { return S; } |
|
public int del() { return del; } |
|
public int min() { return min; } |
|
public int max() { return max; } |
|
public int umin() { return umin; } |
|
public int umax() { return umax; } |
|
public int byteMin(int b) { return byteMin[b-1]; } |
|
public int byteMax(int b) { return byteMax[b-1]; } |
|
|
|
public int compareTo(Coding that) { |
|
int dkey = this.del - that.del; |
|
if (dkey == 0) |
|
dkey = this.B - that.B; |
|
if (dkey == 0) |
|
dkey = this.H - that.H; |
|
if (dkey == 0) |
|
dkey = this.S - that.S; |
|
return dkey; |
|
} |
|
|
|
|
|
public int distanceFrom(Coding that) { |
|
int diffdel = this.del - that.del; |
|
if (diffdel < 0) diffdel = -diffdel; |
|
int diffS = this.S - that.S; |
|
if (diffS < 0) diffS = -diffS; |
|
int diffB = this.B - that.B; |
|
if (diffB < 0) diffB = -diffB; |
|
int diffHL; |
|
if (this.H == that.H) { |
|
diffHL = 0; |
|
} else { |
|
|
|
int thisHL = this.getHL(); |
|
int thatHL = that.getHL(); |
|
|
|
thisHL *= thisHL; |
|
thatHL *= thatHL; |
|
if (thisHL > thatHL) |
|
diffHL = ceil_lg2(1+(thisHL-1)/thatHL); |
|
else |
|
diffHL = ceil_lg2(1+(thatHL-1)/thisHL); |
|
} |
|
int norm = 5*(diffdel + diffS + diffB) + diffHL; |
|
assert(norm != 0 || this.compareTo(that) == 0); |
|
return norm; |
|
} |
|
private int getHL() { |
|
|
|
if (H <= 128) return H; |
|
if (L >= 1) return 128*128/L; |
|
return 128*256; |
|
} |
|
|
|
|
|
static int ceil_lg2(int x) { |
|
assert(x-1 >= 0); |
|
x -= 1; |
|
int lg = 0; |
|
while (x != 0) { |
|
lg++; |
|
x >>= 1; |
|
} |
|
return lg; |
|
} |
|
|
|
static private final byte[] byteBitWidths = new byte[0x100]; |
|
static { |
|
for (int b = 0; b < byteBitWidths.length; b++) { |
|
byteBitWidths[b] = (byte) ceil_lg2(b + 1); |
|
} |
|
for (int i = 10; i >= 0; i = (i << 1) - (i >> 3)) { |
|
assert(bitWidth(i) == ceil_lg2(i + 1)); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
static int bitWidth(int i) { |
|
if (i < 0) i = ~i; |
|
int w = 0; |
|
int lo = i; |
|
if (lo < byteBitWidths.length) |
|
return byteBitWidths[lo]; |
|
int hi; |
|
hi = (lo >>> 16); |
|
if (hi != 0) { |
|
lo = hi; |
|
w += 16; |
|
} |
|
hi = (lo >>> 8); |
|
if (hi != 0) { |
|
lo = hi; |
|
w += 8; |
|
} |
|
w += byteBitWidths[lo]; |
|
|
|
return w; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static int[] makeDeltas(int[] values, int start, int end, |
|
int min, int max) { |
|
assert(max >= min); |
|
int count = end-start; |
|
int[] deltas = new int[count]; |
|
int state = 0; |
|
if (min == max) { |
|
for (int i = 0; i < count; i++) { |
|
int value = values[start+i]; |
|
deltas[i] = value - state; |
|
state = value; |
|
} |
|
} else { |
|
for (int i = 0; i < count; i++) { |
|
int value = values[start+i]; |
|
assert(value >= 0 && value+min <= max); |
|
int delta = value - state; |
|
assert(delta == (long)value - (long)state); |
|
state = value; |
|
|
|
delta = reduceToSignedRange(delta, min, max); |
|
deltas[i] = delta; |
|
} |
|
} |
|
return deltas; |
|
} |
|
|
|
boolean canRepresent(int minValue, int maxValue) { |
|
assert(minValue <= maxValue); |
|
if (del > 0) { |
|
if (isSubrange()) { |
|
|
|
return canRepresentUnsigned(maxValue) |
|
&& canRepresentUnsigned(minValue); |
|
} else { |
|
|
|
return isFullRange(); |
|
} |
|
} |
|
else |
|
|
|
return canRepresentSigned(maxValue) |
|
&& canRepresentSigned(minValue); |
|
} |
|
|
|
boolean canRepresent(int[] values, int start, int end) { |
|
int len = end-start; |
|
if (len == 0) return true; |
|
if (isFullRange()) return true; |
|
|
|
int lmax = values[start]; |
|
int lmin = lmax; |
|
for (int i = 1; i < len; i++) { |
|
int value = values[start+i]; |
|
if (lmax < value) lmax = value; |
|
if (lmin > value) lmin = value; |
|
} |
|
return canRepresent(lmin, lmax); |
|
} |
|
|
|
public double getBitLength(int value) { |
|
return (double) getLength(value) * 8; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public int getLength(int value) { |
|
if (isDelta() && isSubrange()) { |
|
if (!canRepresentUnsigned(value)) |
|
return Integer.MAX_VALUE; |
|
value = reduceToSignedRange(value); |
|
} |
|
if (value >= 0) { |
|
for (int n = 0; n < B; n++) { |
|
if (value <= byteMax[n]) return n+1; |
|
} |
|
} else { |
|
for (int n = 0; n < B; n++) { |
|
if (value >= byteMin[n]) return n+1; |
|
} |
|
} |
|
return Integer.MAX_VALUE; |
|
} |
|
|
|
public int getLength(int[] values, int start, int end) { |
|
int len = end-start; |
|
if (B == 1) return len; |
|
if (L == 0) return len * B; |
|
if (isDelta()) { |
|
int[] deltas; |
|
if (!isSubrange()) |
|
deltas = makeDeltas(values, start, end, 0, 0); |
|
else |
|
deltas = makeDeltas(values, start, end, min, max); |
|
|
|
values = deltas; |
|
start = 0; |
|
} |
|
int sum = len; |
|
|
|
for (int n = 1; n <= B; n++) { |
|
|
|
int lmax = byteMax[n-1]; |
|
int lmin = byteMin[n-1]; |
|
int longer = 0; |
|
for (int i = 0; i < len; i++) { |
|
int value = values[start+i]; |
|
if (value >= 0) { |
|
if (value > lmax) longer++; |
|
} else { |
|
if (value < lmin) longer++; |
|
} |
|
} |
|
if (longer == 0) break; |
|
if (n == B) return Integer.MAX_VALUE; |
|
sum += longer; |
|
} |
|
return sum; |
|
} |
|
|
|
public byte[] getMetaCoding(Coding dflt) { |
|
if (dflt == this) return new byte[]{ (byte) _meta_default }; |
|
int canonicalIndex = BandStructure.indexOf(this); |
|
if (canonicalIndex > 0) |
|
return new byte[]{ (byte) canonicalIndex }; |
|
return new byte[]{ |
|
(byte)_meta_arb, |
|
(byte)(del + 2*S + 8*(B-1)), |
|
(byte)(H-1) |
|
}; |
|
} |
|
public static int parseMetaCoding(byte[] bytes, int pos, Coding dflt, CodingMethod res[]) { |
|
int op = bytes[pos++] & 0xFF; |
|
if (_meta_canon_min <= op && op <= _meta_canon_max) { |
|
Coding c = BandStructure.codingForIndex(op); |
|
assert(c != null); |
|
res[0] = c; |
|
return pos; |
|
} |
|
if (op == _meta_arb) { |
|
int dsb = bytes[pos++] & 0xFF; |
|
int H_1 = bytes[pos++] & 0xFF; |
|
int del = dsb % 2; |
|
int S = (dsb / 2) % 4; |
|
int B = (dsb / 8)+1; |
|
int H = H_1+1; |
|
if (!((1 <= B && B <= B_MAX) && |
|
(0 <= S && S <= S_MAX) && |
|
(1 <= H && H <= H_MAX) && |
|
(0 <= del && del <= 1)) |
|
|| (B == 1 && H != 256) |
|
|| (B == 5 && H == 256)) { |
|
throw new RuntimeException("Bad arb. coding: ("+B+","+H+","+S+","+del); |
|
} |
|
res[0] = Coding.of(B, H, S, del); |
|
return pos; |
|
} |
|
return pos-1; |
|
} |
|
|
|
|
|
public String keyString() { |
|
return "("+B+","+H+","+S+","+del+")"; |
|
} |
|
|
|
public String toString() { |
|
String str = "Coding"+keyString(); |
|
// If -ea, print out more informative strings! |
|
|
|
return str; |
|
} |
|
|
|
static boolean verboseStringForDebug = false; |
|
String stringForDebug() { |
|
String minS = (min == Integer.MIN_VALUE ? "min" : ""+min); |
|
String maxS = (max == Integer.MAX_VALUE ? "max" : ""+max); |
|
String str = keyString()+" L="+L+" r=["+minS+","+maxS+"]"; |
|
if (isSubrange()) |
|
str += " subrange"; |
|
else if (!isFullRange()) |
|
str += " MIDRANGE"; |
|
if (verboseStringForDebug) { |
|
str += " {"; |
|
int prev_range = 0; |
|
for (int n = 1; n <= B; n++) { |
|
int range_n = saturate32((long)byteMax[n-1] - byteMin[n-1] + 1); |
|
assert(range_n == saturate32(codeRangeLong(B, H, n))); |
|
range_n -= prev_range; |
|
prev_range = range_n; |
|
String rngS = (range_n == Integer.MAX_VALUE ? "max" : ""+range_n); |
|
str += " #"+n+"="+rngS; |
|
} |
|
str += " }"; |
|
} |
|
return str; |
|
} |
|
} |