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; 021 022/** 023 * A PopulationCodec is a Codec that is well suited to encoding data that shows statistical or repetitive patterns, 024 * containing for example a few numbers which are repeated a lot throughout the set, but not necessarily sequentially. 025 */ 026public class PopulationCodec extends Codec { 027 028 private final Codec favouredCodec; 029 private Codec tokenCodec; 030 private final Codec unfavouredCodec; 031 private int l; 032 private int[] favoured; 033 034 public PopulationCodec(final Codec favouredCodec, final Codec tokenCodec, final Codec unvafouredCodec) { 035 this.favouredCodec = favouredCodec; 036 this.tokenCodec = tokenCodec; 037 this.unfavouredCodec = unvafouredCodec; 038 } 039 040 public PopulationCodec(final Codec favouredCodec, final int l, final Codec unfavouredCodec) { 041 if (l >= 256 || l <= 0) { 042 throw new IllegalArgumentException("L must be between 1..255"); 043 } 044 this.favouredCodec = favouredCodec; 045 this.l = l; 046 this.unfavouredCodec = unfavouredCodec; 047 } 048 049 @Override 050 public int decode(final InputStream in) throws IOException, Pack200Exception { 051 throw new Pack200Exception("Population encoding does not work unless the number of elements are known"); 052 } 053 054 @Override 055 public int decode(final InputStream in, final long last) throws IOException, Pack200Exception { 056 throw new Pack200Exception("Population encoding does not work unless the number of elements are known"); 057 } 058 059 @Override 060 public int[] decodeInts(final int n, final InputStream in) throws IOException, Pack200Exception { 061 lastBandLength = 0; 062 favoured = new int[n]; // there must be <= n values, but probably a lot 063 // less 064 int result[]; 065 // read table of favorites first 066 int smallest = Integer.MAX_VALUE, absoluteSmallest; 067 int last = 0; 068 int value = 0, absoluteValue; 069 int k = -1; 070 while (true) { 071 value = favouredCodec.decode(in, last); 072 if (k > -1 && (value == smallest || value == last)) { 073 break; 074 } 075 favoured[++k] = value; 076 absoluteSmallest = Math.abs(smallest); 077 absoluteValue = Math.abs(value); 078 if (absoluteSmallest > absoluteValue) { 079 smallest = value; 080 } else if (absoluteSmallest == absoluteValue) { 081 // ensure that -X and +X -> +X 082 smallest = absoluteSmallest; 083 } 084 last = value; 085 } 086 lastBandLength += k; 087 // if tokenCodec needs to be derived from the T, L and K values 088 if (tokenCodec == null) { 089 if (k < 256) { 090 tokenCodec = Codec.BYTE1; 091 } else { 092 // if k >= 256, b >= 2 093 int b = 1; 094 BHSDCodec codec = null; 095 while (++b < 5) { 096 codec = new BHSDCodec(b, 256 - l, 0); 097 if (codec.encodes(k)) { 098 tokenCodec = codec; 099 break; 100 } 101 } 102 if (tokenCodec == null) { 103 throw new Pack200Exception("Cannot calculate token codec from " + k + " and " + l); 104 } 105 } 106 } 107 // read favorites 108 lastBandLength += n; 109 result = tokenCodec.decodeInts(n, in); 110 // read unfavorites 111 last = 0; 112 for (int i = 0; i < n; i++) { 113 final int index = result[i]; 114 if (index == 0) { 115 lastBandLength++; 116 result[i] = last = unfavouredCodec.decode(in, last); 117 } else { 118 result[i] = favoured[index - 1]; 119 } 120 } 121 return result; 122 } 123 124 public int[] getFavoured() { 125 return favoured; 126 } 127 128 public Codec getFavouredCodec() { 129 return favouredCodec; 130 } 131 132 public Codec getUnfavouredCodec() { 133 return unfavouredCodec; 134 } 135 136 @Override 137 public byte[] encode(final int value, final int last) throws Pack200Exception { 138 throw new Pack200Exception("Population encoding does not work unless the number of elements are known"); 139 } 140 141 @Override 142 public byte[] encode(final int value) throws Pack200Exception { 143 throw new Pack200Exception("Population encoding does not work unless the number of elements are known"); 144 } 145 146 public byte[] encode(final int[] favoured, final int[] tokens, final int[] unfavoured) throws Pack200Exception { 147 final int[] favoured2 = new int[favoured.length + 1]; 148 System.arraycopy(favoured, 0, favoured2, 0, favoured.length); 149 favoured2[favoured2.length - 1] = favoured[favoured.length - 1]; // repeat last value; 150 final byte[] favouredEncoded = favouredCodec.encode(favoured2); 151 final byte[] tokensEncoded = tokenCodec.encode(tokens); 152 final byte[] unfavouredEncoded = unfavouredCodec.encode(unfavoured); 153 final byte[] band = new byte[favouredEncoded.length + tokensEncoded.length + unfavouredEncoded.length]; 154 System.arraycopy(favouredEncoded, 0, band, 0, favouredEncoded.length); 155 System.arraycopy(tokensEncoded, 0, band, favouredEncoded.length, tokensEncoded.length); 156 System.arraycopy(unfavouredEncoded, 0, band, favouredEncoded.length + tokensEncoded.length, 157 unfavouredEncoded.length); 158 return band; 159 } 160 161 public Codec getTokenCodec() { 162 return tokenCodec; 163 } 164}