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