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 are decoded from the remaining codec. Note 027 * 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 @Override 065 public int[] decodeInts(final int n, final InputStream in) throws IOException, Pack200Exception { 066 final int[] aValues = aCodec.decodeInts(k, in); 067 normalise(aValues, aCodec); 068 final int[] bValues = bCodec.decodeInts(n - k, in); 069 normalise(bValues, bCodec); 070 final int[] band = new int[check(n, in)]; 071 System.arraycopy(aValues, 0, band, 0, k); 072 System.arraycopy(bValues, 0, band, k, n - k); 073 lastBandLength = aCodec.lastBandLength + bCodec.lastBandLength; 074 return band; 075 } 076 077 @Override 078 public byte[] encode(final int value) throws Pack200Exception { 079 throw new Pack200Exception("Must encode entire band at once with a RunCodec"); 080 } 081 082 @Override 083 public byte[] encode(final int value, final int last) throws Pack200Exception { 084 throw new Pack200Exception("Must encode entire band at once with a RunCodec"); 085 } 086 087 public Codec getACodec() { 088 return aCodec; 089 } 090 091 public Codec getBCodec() { 092 return bCodec; 093 } 094 095 public int getK() { 096 return k; 097 } 098 099 private int normalise(int value, final Codec codecUsed) { 100 if (codecUsed instanceof BHSDCodec) { 101 final BHSDCodec bhsd = (BHSDCodec) codecUsed; 102 if (bhsd.isDelta()) { 103 final long cardinality = bhsd.cardinality(); 104 while (value > bhsd.largest()) { 105 value -= cardinality; 106 } 107 while (value < bhsd.smallest()) { 108 value = ExactMath.add(value, cardinality); 109 } 110 } 111 } 112 return value; 113 } 114 115 private void normalise(final int[] band, final Codec codecUsed) { 116 if (codecUsed instanceof BHSDCodec) { 117 final BHSDCodec bhsd = (BHSDCodec) codecUsed; 118 if (bhsd.isDelta()) { 119 final long cardinality = bhsd.cardinality(); 120 for (int i = 0; i < band.length; i++) { 121 while (band[i] > bhsd.largest()) { 122 band[i] -= cardinality; 123 } 124 while (band[i] < bhsd.smallest()) { 125 band[i] = ExactMath.add(band[i], cardinality); 126 } 127 } 128 } 129 } else if (codecUsed instanceof PopulationCodec) { 130 final PopulationCodec popCodec = (PopulationCodec) codecUsed; 131 final int[] favoured = popCodec.getFavoured().clone(); 132 Arrays.sort(favoured); 133 for (int i = 0; i < band.length; i++) { 134 final boolean favouredValue = Arrays.binarySearch(favoured, band[i]) > -1; 135 final Codec theCodec = favouredValue ? popCodec.getFavouredCodec() : popCodec.getUnfavouredCodec(); 136 if (theCodec instanceof BHSDCodec) { 137 final BHSDCodec bhsd = (BHSDCodec) theCodec; 138 if (bhsd.isDelta()) { 139 final long cardinality = bhsd.cardinality(); 140 while (band[i] > bhsd.largest()) { 141 band[i] -= cardinality; 142 } 143 while (band[i] < bhsd.smallest()) { 144 band[i] = ExactMath.add(band[i], cardinality); 145 } 146 } 147 } 148 } 149 } 150 } 151 152 @Override 153 public String toString() { 154 return "RunCodec[k=" + k + ";aCodec=" + aCodec + "bCodec=" + bCodec + "]"; 155 } 156}