001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.compress.harmony.pack200; 018 019import java.io.IOException; 020import java.io.InputStream; 021import java.util.Arrays; 022 023/** 024 * 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 025 * are repeated a lot throughout the set, but not necessarily sequentially. 026 */ 027public class PopulationCodec extends Codec { 028 029 private final Codec favouredCodec; 030 private Codec tokenCodec; 031 private final Codec unfavouredCodec; 032 private int l; 033 private int[] favoured; 034 035 public PopulationCodec(final Codec favouredCodec, final Codec tokenCodec, final Codec unvafouredCodec) { 036 this.favouredCodec = favouredCodec; 037 this.tokenCodec = tokenCodec; 038 this.unfavouredCodec = unvafouredCodec; 039 } 040 041 public PopulationCodec(final Codec favouredCodec, final int l, final Codec unfavouredCodec) { 042 if (l >= 256 || l <= 0) { 043 throw new IllegalArgumentException("L must be between 1..255"); 044 } 045 this.favouredCodec = favouredCodec; 046 this.l = l; 047 this.unfavouredCodec = unfavouredCodec; 048 } 049 050 @Override 051 public int decode(final InputStream in) throws IOException, Pack200Exception { 052 throw new Pack200Exception("Population encoding does not work unless the number of elements are known"); 053 } 054 055 @Override 056 public int decode(final InputStream in, final long last) throws IOException, Pack200Exception { 057 throw new Pack200Exception("Population encoding does not work unless the number of elements are known"); 058 } 059 060 @Override 061 public int[] decodeInts(final int n, final InputStream in) throws IOException, Pack200Exception { 062 lastBandLength = 0; 063 favoured = new int[check(n, in)]; // there must be <= n values, but probably a lot 064 // less 065 int[] result; 066 // read table of favorites first 067 int smallest = Integer.MAX_VALUE, absoluteSmallest; 068 int last = 0; 069 int value = 0, absoluteValue; 070 int k = -1; 071 while (true) { 072 value = favouredCodec.decode(in, last); 073 if (k > -1 && (value == smallest || value == last)) { 074 break; 075 } 076 favoured[++k] = value; 077 absoluteSmallest = Math.abs(smallest); 078 absoluteValue = Math.abs(value); 079 if (absoluteSmallest > absoluteValue) { 080 smallest = value; 081 } else if (absoluteSmallest == absoluteValue) { 082 // ensure that -X and +X -> +X 083 smallest = absoluteSmallest; 084 } 085 last = value; 086 } 087 lastBandLength += k; 088 // if tokenCodec needs to be derived from the T, L and K values 089 if (tokenCodec == null) { 090 if (k < 256) { 091 tokenCodec = BYTE1; 092 } else { 093 // if k >= 256, b >= 2 094 int b = 1; 095 BHSDCodec codec; 096 while (++b < 5) { 097 codec = new BHSDCodec(b, 256 - l, 0); 098 if (codec.encodes(k)) { 099 tokenCodec = codec; 100 break; 101 } 102 } 103 if (tokenCodec == null) { 104 throw new Pack200Exception("Cannot calculate token codec from " + k + " and " + l); 105 } 106 } 107 } 108 // read favorites 109 lastBandLength += n; 110 result = tokenCodec.decodeInts(n, in); 111 // read unfavorites 112 last = 0; 113 for (int i = 0; i < n; i++) { 114 final int index = result[i]; 115 if (index == 0) { 116 lastBandLength++; 117 result[i] = last = unfavouredCodec.decode(in, last); 118 } else { 119 result[i] = favoured[index - 1]; 120 } 121 } 122 return result; 123 } 124 125 @Override 126 public byte[] encode(final int value) throws Pack200Exception { 127 throw new Pack200Exception("Population encoding does not work unless the number of elements are known"); 128 } 129 130 @Override 131 public byte[] encode(final int value, final int last) throws Pack200Exception { 132 throw new Pack200Exception("Population encoding does not work unless the number of elements are known"); 133 } 134 135 public byte[] encode(final int[] favoured, final int[] tokens, final int[] unfavoured) throws Pack200Exception { 136 final int[] favoured2 = Arrays.copyOf(favoured, favoured.length + 1); 137 favoured2[favoured2.length - 1] = favoured[favoured.length - 1]; // repeat last value; 138 final byte[] favouredEncoded = favouredCodec.encode(favoured2); 139 final byte[] tokensEncoded = tokenCodec.encode(tokens); 140 final byte[] unfavouredEncoded = unfavouredCodec.encode(unfavoured); 141 final byte[] band = new byte[favouredEncoded.length + tokensEncoded.length + unfavouredEncoded.length]; 142 System.arraycopy(favouredEncoded, 0, band, 0, favouredEncoded.length); 143 System.arraycopy(tokensEncoded, 0, band, favouredEncoded.length, tokensEncoded.length); 144 System.arraycopy(unfavouredEncoded, 0, band, favouredEncoded.length + tokensEncoded.length, unfavouredEncoded.length); 145 return band; 146 } 147 148 public int[] getFavoured() { 149 return favoured; 150 } 151 152 public Codec getFavouredCodec() { 153 return favouredCodec; 154 } 155 156 public Codec getTokenCodec() { 157 return tokenCodec; 158 } 159 160 public Codec getUnfavouredCodec() { 161 return unfavouredCodec; 162 } 163}