001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.commons.compress.harmony.pack200; 020 021import java.io.IOException; 022import java.io.InputStream; 023import java.util.Arrays; 024 025import org.apache.commons.compress.utils.ExactMath; 026 027/** 028 * 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 029 * that since this codec maintains state, the instances are not reusable. 030 */ 031public class RunCodec extends Codec { 032 033 private int k; 034 private final Codec aCodec; 035 private final Codec bCodec; 036 private int last; 037 038 public RunCodec(final int k, final Codec aCodec, final Codec bCodec) throws Pack200Exception { 039 if (k <= 0) { 040 throw new Pack200Exception("Cannot have a RunCodec for a negative number of numbers"); 041 } 042 if (aCodec == null || bCodec == null) { 043 throw new Pack200Exception("Must supply both codecs for a RunCodec"); 044 } 045 this.k = k; 046 this.aCodec = aCodec; 047 this.bCodec = bCodec; 048 } 049 050 @Override 051 public int decode(final InputStream in) throws IOException, Pack200Exception { 052 return decode(in, this.last); 053 } 054 055 @Override 056 public int decode(final InputStream in, final long last) throws IOException, Pack200Exception { 057 if (--k >= 0) { 058 final int value = aCodec.decode(in, this.last); 059 this.last = k == 0 ? 0 : value; 060 return normalise(value, aCodec); 061 } 062 this.last = bCodec.decode(in, this.last); 063 return normalise(this.last, bCodec); 064 } 065 066 @Override 067 public int[] decodeInts(final int n, final InputStream in) throws IOException, Pack200Exception { 068 final int[] aValues = aCodec.decodeInts(k, in); 069 normalise(aValues, aCodec); 070 final int[] bValues = bCodec.decodeInts(n - k, in); 071 normalise(bValues, bCodec); 072 final int[] band = new int[check(n, in)]; 073 System.arraycopy(aValues, 0, band, 0, k); 074 System.arraycopy(bValues, 0, band, k, n - k); 075 lastBandLength = aCodec.lastBandLength + bCodec.lastBandLength; 076 return band; 077 } 078 079 @Override 080 public byte[] encode(final int value) throws Pack200Exception { 081 throw new Pack200Exception("Must encode entire band at once with a RunCodec"); 082 } 083 084 @Override 085 public byte[] encode(final int value, final int last) throws Pack200Exception { 086 throw new Pack200Exception("Must encode entire band at once with a RunCodec"); 087 } 088 089 public Codec getACodec() { 090 return aCodec; 091 } 092 093 public Codec getBCodec() { 094 return bCodec; 095 } 096 097 public int getK() { 098 return k; 099 } 100 101 private int normalise(int value, final Codec codecUsed) { 102 if (codecUsed instanceof BHSDCodec) { 103 final BHSDCodec bhsd = (BHSDCodec) codecUsed; 104 if (bhsd.isDelta()) { 105 final long cardinality = bhsd.cardinality(); 106 while (value > bhsd.largest()) { 107 value -= cardinality; 108 } 109 while (value < bhsd.smallest()) { 110 value = ExactMath.add(value, cardinality); 111 } 112 } 113 } 114 return value; 115 } 116 117 private void normalise(final int[] band, final Codec codecUsed) { 118 if (codecUsed instanceof BHSDCodec) { 119 final BHSDCodec bhsd = (BHSDCodec) codecUsed; 120 if (bhsd.isDelta()) { 121 final long cardinality = bhsd.cardinality(); 122 for (int i = 0; i < band.length; i++) { 123 while (band[i] > bhsd.largest()) { 124 band[i] -= cardinality; 125 } 126 while (band[i] < bhsd.smallest()) { 127 band[i] = ExactMath.add(band[i], cardinality); 128 } 129 } 130 } 131 } else if (codecUsed instanceof PopulationCodec) { 132 final PopulationCodec popCodec = (PopulationCodec) codecUsed; 133 final int[] favoured = popCodec.getFavoured().clone(); 134 Arrays.sort(favoured); 135 for (int i = 0; i < band.length; i++) { 136 final boolean favouredValue = Arrays.binarySearch(favoured, band[i]) > -1; 137 final Codec theCodec = favouredValue ? popCodec.getFavouredCodec() : popCodec.getUnfavouredCodec(); 138 if (theCodec instanceof BHSDCodec) { 139 final BHSDCodec bhsd = (BHSDCodec) theCodec; 140 if (bhsd.isDelta()) { 141 final long cardinality = bhsd.cardinality(); 142 while (band[i] > bhsd.largest()) { 143 band[i] -= cardinality; 144 } 145 while (band[i] < bhsd.smallest()) { 146 band[i] = ExactMath.add(band[i], cardinality); 147 } 148 } 149 } 150 } 151 } 152 } 153 154 @Override 155 public String toString() { 156 return "RunCodec[k=" + k + ";aCodec=" + aCodec + "bCodec=" + bCodec + "]"; 157 } 158}