package com.colloquial.arithcode;
import java.io.IOException;
import java.io.InputStream;
/** <P>Performs arithmetic decoding, converting bit input into
* cumulative probability interval output. Returns probabilities as
* integer counts <code>low</code>, <code>high</code> and
* <code>total</code>, with the range being
* <code>[low/total,high/total)</code>.
*
* <P>For more details, see <a href="../../../tutorial.html">The Arithemtic Coding Tutorial</a>.
*
* @author <a href="http://www.colloquial.com/carp/">Bob Carpenter</a>
* @version 1.1
* @see ArithEncoder
* @see BitInput
* @since 1.0
*/
public final class ArithDecoder extends ArithCoder {
/** Construct an arithmetic decoder that reads from the given
* bit input.
* @param in Bit input from which to read bits.
* @throws IOException If there is an exception buffering from the bit input stream.
* @since 1.1
*/
public ArithDecoder(BitInput in) throws IOException {
_in = in;
for (int i = 1; i <= CODE_VALUE_BITS; ++i) {
bufferBit();
++_bufferedBits;
}
}
/** Construct an arithmetic decoder that reads from the given
* input stream.
* @param in Input stream from which to read.
* @throws IOException If there is an exception buffering from input stream.
*/
public ArithDecoder(InputStream in) throws IOException {
this(new BitInput(in));
}
/** Returns <code>true</code> if the end of stream has been reached
* and there are no more symbols to decode.
* @return <code>true</code> if the end of stream has been reached.
*/
public boolean endOfStream() { return _endOfStream; }
/** Returns a count for the current symbol that will be between the
* low and high counts for the symbol in the model given the total count.
* Once symbol is retrieved, the model is used to compute the actual low,
* high and total counts and {@link #removeSymbolFromStream} is called.
* @param totalCount The current total count for the model.
* @return A count that is in the range above or equal to the low count and less than the high count of the next symbol decoded.
*/
public int getCurrentSymbolCount(int totalCount) {
return (int) (((_value - _low + 1) * totalCount - 1) / (_high - _low + 1));
}
/** Removes a symbol from the input stream that was coded with counts
* <code>{ low, high, total }</code>. Called after {@link #getCurrentSymbolCount}.
* @param counts Array of low, high and total count used to code the symbol.
* @throws IOException If there is an exception in buffering input from the underlying input stream.
* @see #removeSymbolFromStream(long,long,long)
*/
public void removeSymbolFromStream(int[] counts) throws IOException {
removeSymbolFromStream(counts[0],counts[1],counts[2]);
}
/** Removes a symbol from the input stream. Called after {@link #getCurrentSymbolCount}.
* @param lowCount Cumulative count for symbols indexed below symbol to be removed.
* @param highCount <code>lowCount</code> plus count for this symbol.
* @param totalCount Total count for all symbols seen.
* @throws IOException If there is an exception in buffering input from the underlying input stream.
*/
public void removeSymbolFromStream(long lowCount, long highCount, long totalCount) throws IOException {
long range = _high - _low + 1;
_high = _low + (range * highCount) / totalCount - 1;
_low = _low + (range * lowCount) / totalCount;
while (true) {
if (_high < HALF) {
// no effect
} else if (_low >= HALF) {
_value -= HALF;
_low -= HALF;
_high -= HALF;
} else if (_low >= FIRST_QUARTER && _high <= THIRD_QUARTER) {
_value -= FIRST_QUARTER;
_low -= FIRST_QUARTER;
_high -= FIRST_QUARTER;
} else {
return;
}
_low <<= 1; // = 2 * _low; // _low <<= 1;
_high = (_high << 1) + 1; // 2 * _high + 1; // _high = (_high<<1) + 1;
bufferBit();
}
}
/** Closes underlying bit output.
* @throws IOException If there is an underlying I/O exception in the bit input.
*/
public void close() throws IOException { _in.close(); }
/** Input stream from which to read bits.
*/
private final BitInput _in;
/** Current bits for decoding.
*/
private long _value; // implied = 0;
/** Value will be <code>true</code> if the end of stream has
* been reached.
*/
private boolean _endOfStream = false;
/** Number of bits that have been buffered.
*/
private int _bufferedBits; // implied = 0;
/** Reads a bit from the underlying bit input stream and buffers it.
* @throws IOException If there is an <code>IOException</code> buffering from the underlying bit stream.
*/
private void bufferBit() throws IOException {
if (_in.endOfStream()) {
if (_bufferedBits == 0) {
_endOfStream = true;
return;
}
_value <<= 1;
--_bufferedBits;
} else {
_value = (_value<<1);
if (_in.readBit()) ++_value;
}
}
}