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 023import org.apache.commons.compress.utils.ExactMath; 024 025/** 026 * A run codec is a grouping of two nested codecs; K values are decoded from the first codec, and the remaining codes 027 * are decoded from the remaining codec. Note that since this codec maintains state, the instances are not reusable. 028 */ 029public class RunCodec extends Codec { 030 031 private int k; 032 private final Codec aCodec; 033 private final Codec bCodec; 034 private int last; 035 036 public RunCodec(final int k, final Codec aCodec, final Codec bCodec) throws Pack200Exception { 037 if (k <= 0) { 038 throw new Pack200Exception("Cannot have a RunCodec for a negative number of numbers"); 039 } 040 if (aCodec == null || bCodec == null) { 041 throw new Pack200Exception("Must supply both codecs for a RunCodec"); 042 } 043 this.k = k; 044 this.aCodec = aCodec; 045 this.bCodec = bCodec; 046 } 047 048 @Override 049 public int decode(final InputStream in) throws IOException, Pack200Exception { 050 return decode(in, this.last); 051 } 052 053 @Override 054 public int decode(final InputStream in, final long last) throws IOException, Pack200Exception { 055 if (--k >= 0) { 056 final int value = aCodec.decode(in, this.last); 057 this.last = (k == 0 ? 0 : value); 058 return normalise(value, aCodec); 059 } 060 this.last = bCodec.decode(in, this.last); 061 return normalise(this.last, bCodec); 062 } 063 064 private int normalise(int value, final Codec codecUsed) { 065 if (codecUsed instanceof BHSDCodec) { 066 final BHSDCodec bhsd = (BHSDCodec) codecUsed; 067 if (bhsd.isDelta()) { 068 final long cardinality = bhsd.cardinality(); 069 while (value > bhsd.largest()) { 070 value -= cardinality; 071 } 072 while (value < bhsd.smallest()) { 073 value = ExactMath.add(value, cardinality); 074 } 075 } 076 } 077 return value; 078 } 079 080 @Override 081 public int[] decodeInts(final int n, final InputStream in) throws IOException, Pack200Exception { 082 final int[] band = new int[n]; 083 final int[] aValues = aCodec.decodeInts(k, in); 084 normalise(aValues, aCodec); 085 final int[] bValues = bCodec.decodeInts(n - k, in); 086 normalise(bValues, bCodec); 087 System.arraycopy(aValues, 0, band, 0, k); 088 System.arraycopy(bValues, 0, band, k, n - k); 089 lastBandLength = aCodec.lastBandLength + bCodec.lastBandLength; 090 return band; 091 } 092 093 private void normalise(final int[] band, final Codec codecUsed) { 094 if (codecUsed instanceof BHSDCodec) { 095 final BHSDCodec bhsd = (BHSDCodec) codecUsed; 096 if (bhsd.isDelta()) { 097 final long cardinality = bhsd.cardinality(); 098 for (int i = 0; i < band.length; i++) { 099 while (band[i] > bhsd.largest()) { 100 band[i] -= cardinality; 101 } 102 while (band[i] < bhsd.smallest()) { 103 band[i] = ExactMath.add(band[i], cardinality); 104 } 105 } 106 } 107 } else if (codecUsed instanceof PopulationCodec) { 108 final PopulationCodec popCodec = (PopulationCodec) codecUsed; 109 final int[] favoured = popCodec.getFavoured().clone(); 110 Arrays.sort(favoured); 111 for (int i = 0; i < band.length; i++) { 112 final boolean favouredValue = Arrays.binarySearch(favoured, band[i]) > -1; 113 final Codec theCodec = favouredValue ? popCodec.getFavouredCodec() : popCodec.getUnfavouredCodec(); 114 if (theCodec instanceof BHSDCodec) { 115 final BHSDCodec bhsd = (BHSDCodec) theCodec; 116 if (bhsd.isDelta()) { 117 final long cardinality = bhsd.cardinality(); 118 while (band[i] > bhsd.largest()) { 119 band[i] -= cardinality; 120 } 121 while (band[i] < bhsd.smallest()) { 122 band[i] = ExactMath.add(band[i], cardinality); 123 } 124 } 125 } 126 } 127 } 128 } 129 130 @Override 131 public String toString() { 132 return "RunCodec[k=" + k + ";aCodec=" + aCodec + "bCodec=" + bCodec + "]"; 133 } 134 135 @Override 136 public byte[] encode(final int value, final int last) throws Pack200Exception { 137 throw new Pack200Exception("Must encode entire band at once with a RunCodec"); 138 } 139 140 @Override 141 public byte[] encode(final int value) throws Pack200Exception { 142 throw new Pack200Exception("Must encode entire band at once with a RunCodec"); 143 } 144 145 public int getK() { 146 return k; 147 } 148 149 public Codec getACodec() { 150 return aCodec; 151 } 152 153 public Codec getBCodec() { 154 return bCodec; 155 } 156}