package org.jcodec.codecs.common.biari;
import java.io.IOException;
import java.io.OutputStream;
/**
* This class is part of JCodec ( www.jcodec.org ) This software is distributed
* under FreeBSD License
*
* Binary ariphmetic encoder
*
* Half-way to MQ Coder
*
* @author The JCodec project
*
*/
public class MQEncoder {
public static final int CARRY_MASK = (1 << 27);
// 'A' register, only lower 16 bits are used
private int range;
// 'C' register, only 28 bits are used
private int offset;
// 't' variable
private int bitsToCode;
// 'L' variable
private long bytesOutput;
// 'B' variable
private int byteToGo;
// Stream to output coded bytes
private OutputStream out;
public MQEncoder(OutputStream out) {
range = 0x8000;
offset = 0;
bitsToCode = 12;
this.out = out;
}
/**
* Encodes one symbol either 0 or 1
*
* @param symbol
* @throws IOException
*/
public void encode(int symbol, Context cm) throws IOException {
int rangeLps = MQConst.pLps[cm.getState()];
if (symbol == cm.getMps()) {
range -= rangeLps;
offset += rangeLps;
if (range < 0x8000) {
while (range < 0x8000)
renormalize();
cm.setState(MQConst.transitMPS[cm.getState()]);
}
} else {
range = rangeLps;
while (range < 0x8000)
renormalize();
if (MQConst.mpsSwitch[cm.getState()] != 0)
cm.setMps(1 - cm.getMps());
cm.setState(MQConst.transitLPS[cm.getState()]);
}
}
public void finish() throws IOException {
finalizeValue();
offset <<= bitsToCode;
int bitsToOutput = 12 - bitsToCode;
outputByte();
bitsToOutput -= bitsToCode;
if (bitsToOutput > 0) {
offset <<= bitsToCode;
outputByte();
}
out.write(byteToGo);
}
private void finalizeValue() {
int halfBit = offset & 0x8000;
offset &= 0xffff0000;
if (halfBit == 0) {
offset |= 0x8000;
} else {
offset += 0x10000;
}
}
private void renormalize() throws IOException {
offset <<= 1;
range <<= 1;
range &= 0xffffL;
--bitsToCode;
if (bitsToCode == 0)
outputByte();
}
private void outputByte() throws IOException {
if (bytesOutput == 0)
outputByteNoStuffing();
else {
if (byteToGo == 0xff)
outputByteWithStuffing();
else {
if ((offset & CARRY_MASK) != 0) {
++byteToGo;
offset &= 0x7ffffff;
if (byteToGo == 0xff)
outputByteWithStuffing();
else
outputByteNoStuffing();
} else
outputByteNoStuffing();
}
}
}
private void outputByteWithStuffing() throws IOException {
bitsToCode = 7;
if (bytesOutput > 0) {
out.write(byteToGo);
}
byteToGo = (offset >> 20) & 0xff;
offset &= 0xfffff;
++bytesOutput;
}
private void outputByteNoStuffing() throws IOException {
bitsToCode = 8;
if (bytesOutput > 0) {
out.write(byteToGo);
}
byteToGo = (offset >> 19) & 0xff;
offset &= 0x7ffff;
++bytesOutput;
}
}