Fork of Apache Harmony's Pack200 implementation
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pack200/src/main/java/org/apache/harmony/pack200/PopulationCodec.java

174 lines
6.2 KiB

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.harmony.pack200;
import java.io.IOException;
import java.io.InputStream;
/**
* A PopulationCodec is a Codec that is well suited to encoding data that shows
* statistical or repetitive patterns, containing for example a few numbers
* which are repeated a lot throughout the set, but not necessarily
* sequentially.
*/
public class PopulationCodec extends Codec {
private final Codec favouredCodec;
private Codec tokenCodec;
private final Codec unfavouredCodec;
private int l;
private int[] favoured;
public PopulationCodec(Codec favouredCodec, Codec tokenCodec,
Codec unvafouredCodec) {
this.favouredCodec = favouredCodec;
this.tokenCodec = tokenCodec;
this.unfavouredCodec = unvafouredCodec;
}
public PopulationCodec(Codec favouredCodec, int l, Codec unfavouredCodec) {
if (l >= 256 || l <= 0) {
throw new IllegalArgumentException("L must be between 1..255");
}
this.favouredCodec = favouredCodec;
this.l = l;
this.unfavouredCodec = unfavouredCodec;
}
public int decode(InputStream in) throws IOException, Pack200Exception {
throw new Pack200Exception(
"Population encoding does not work unless the number of elements are known");
}
public int decode(InputStream in, long last) throws IOException,
Pack200Exception {
throw new Pack200Exception(
"Population encoding does not work unless the number of elements are known");
}
public int[] decodeInts(int n, InputStream in) throws IOException,
Pack200Exception {
lastBandLength = 0;
favoured = new int[n]; // there must be <= n values, but probably a lot
// less
int result[];
// read table of favorites first
int smallest = Integer.MAX_VALUE, absoluteSmallest;
int last = 0;
int value = 0, absoluteValue;
int k = -1;
while (true) {
value = favouredCodec.decode(in, last);
if (k > -1 && (value == smallest || value == last)) {
break;
}
favoured[++k] = value;
absoluteSmallest = Math.abs(smallest);
absoluteValue = Math.abs(value);
if (absoluteSmallest > absoluteValue) {
smallest = value;
} else if (absoluteSmallest == absoluteValue) {
// ensure that -X and +X -> +X
smallest = absoluteSmallest;
}
last = value;
}
lastBandLength += k;
// if tokenCodec needs to be derived from the T, L and K values
if (tokenCodec == null) {
if (k < 256) {
tokenCodec = Codec.BYTE1;
} else {
// if k >= 256, b >= 2
int b = 1;
BHSDCodec codec = null;
while (++b < 5) {
codec = new BHSDCodec(b, 256 - l, 0);
if (codec.encodes(k)) {
tokenCodec = codec;
break;
}
}
if (tokenCodec == null) {
throw new Pack200Exception(
"Cannot calculate token codec from " + k + " and "
+ l);
}
}
}
// read favorites
lastBandLength += n;
result = tokenCodec.decodeInts(n, in);
// read unfavorites
last = 0;
for (int i = 0; i < n; i++) {
int index = result[i];
if (index == 0) {
lastBandLength++;
result[i] = last = unfavouredCodec.decode(in, last);
} else {
result[i] = favoured[index - 1];
}
}
return result;
}
public int[] getFavoured() {
return favoured;
}
public Codec getFavouredCodec() {
return favouredCodec;
}
public Codec getUnfavouredCodec() {
return unfavouredCodec;
}
public byte[] encode(int value, int last) throws Pack200Exception {
throw new Pack200Exception(
"Population encoding does not work unless the number of elements are known");
}
public byte[] encode(int value) throws Pack200Exception {
throw new Pack200Exception(
"Population encoding does not work unless the number of elements are known");
}
public byte[] encode(int[] favoured, int[] tokens, int[] unfavoured) throws Pack200Exception {
int[] favoured2 = new int[favoured.length + 1];
System.arraycopy(favoured, 0, favoured2, 0, favoured.length);
favoured2[favoured2.length - 1] = favoured[favoured.length - 1]; // repeat last value;
byte[] favouredEncoded = favouredCodec.encode(favoured2);
byte[] tokensEncoded = tokenCodec.encode(tokens);
byte[] unfavouredEncoded = unfavouredCodec.encode(unfavoured);
byte[] band = new byte[favouredEncoded.length + tokensEncoded.length + unfavouredEncoded.length];
System.arraycopy(favouredEncoded, 0, band, 0, favouredEncoded.length);
System.arraycopy(tokensEncoded, 0, band, favouredEncoded.length, tokensEncoded.length);
System.arraycopy(unfavouredEncoded, 0, band, favouredEncoded.length + tokensEncoded.length, unfavouredEncoded.length);
return band;
}
public Codec getTokenCodec() {
return tokenCodec;
}
public int getL() {
return l;
}
}