blob: d929dfe04978350942062fb541bb8237f478e16f [file] [log] [blame]
/*
* 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.commons.compress.harmony.pack200;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
/**
* 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(final Codec favouredCodec, final Codec tokenCodec, final Codec unvafouredCodec) {
this.favouredCodec = favouredCodec;
this.tokenCodec = tokenCodec;
this.unfavouredCodec = unvafouredCodec;
}
public PopulationCodec(final Codec favouredCodec, final int l, final 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;
}
@Override
public int decode(final InputStream in) throws IOException, Pack200Exception {
throw new Pack200Exception("Population encoding does not work unless the number of elements are known");
}
@Override
public int decode(final InputStream in, final long last) throws IOException, Pack200Exception {
throw new Pack200Exception("Population encoding does not work unless the number of elements are known");
}
@Override
public int[] decodeInts(final int n, final InputStream in) throws IOException, Pack200Exception {
lastBandLength = 0;
favoured = new int[check(n, in)]; // 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 = BYTE1;
} else {
// if k >= 256, b >= 2
int b = 1;
BHSDCodec codec;
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++) {
final int index = result[i];
if (index == 0) {
lastBandLength++;
result[i] = last = unfavouredCodec.decode(in, last);
} else {
result[i] = favoured[index - 1];
}
}
return result;
}
@Override
public byte[] encode(final int value) throws Pack200Exception {
throw new Pack200Exception("Population encoding does not work unless the number of elements are known");
}
@Override
public byte[] encode(final int value, final int last) throws Pack200Exception {
throw new Pack200Exception("Population encoding does not work unless the number of elements are known");
}
public byte[] encode(final int[] favoured, final int[] tokens, final int[] unfavoured) throws Pack200Exception {
final int[] favoured2 = Arrays.copyOf(favoured, favoured.length + 1);
favoured2[favoured2.length - 1] = favoured[favoured.length - 1]; // repeat last value;
final byte[] favouredEncoded = favouredCodec.encode(favoured2);
final byte[] tokensEncoded = tokenCodec.encode(tokens);
final byte[] unfavouredEncoded = unfavouredCodec.encode(unfavoured);
final 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 int[] getFavoured() {
return favoured;
}
public Codec getFavouredCodec() {
return favouredCodec;
}
public Codec getTokenCodec() {
return tokenCodec;
}
public Codec getUnfavouredCodec() {
return unfavouredCodec;
}
}