/*
* JaamSim Discrete Event Simulation
* Copyright (C) 2012 Ausenco Engineering Canada Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jaamsim.video.vp8;
import java.nio.ByteBuffer;
public final class BoolEncoder {
//private final ArrayList<Byte> data;
private final byte[] data;
int pos = 0;
private int range;
private int bottom;
private int count;
private boolean canEncode = true;
BoolEncoder() {
range = 255;
bottom = 0;
data = new byte[1 << 25]; // 32 MiB buffer for now
count = 0;
}
public final void encodeBoolean(boolean b, int prob) {
//assert(prob >= 1 && prob <= 255);
assert(canEncode);
int split = 1 + (((range-1) * prob) >> 8);
if (b) {
range -= split;
bottom += split;
} else {
range = split;
}
while (range < 128) {
range <<= 1;
bottom <<= 1;
if (bottom >= 65536) {
addOneToOutput();
bottom -= 65536;
}
if (++count == 8) {
// Write out a byte
data[pos++] = (byte)((bottom & 0xFF00) >> 8);
count = 0;
bottom = bottom & 0xFF;
}
}
//EncLogger.log(String.format("%d %d %d", b ? 1 : 0, prob, range));
}
private final void addOneToOutput() {
// The value overflowed and we need to explicitly add 1 to the stored value
for (int i = pos-1; i >= 0; i--) {
int val = Util.unsign(data[i]);
if (val != 0xff) {
data[i] = (byte)(val + 1);
break;
}
data[i] = 0;
}
}
public final void encodeFlag(boolean b) {
encodeBoolean(b, 128);
}
public void encodeLitUInt(int v, int numBits) {
assert(v >= 0);
int mask = 1 << (numBits - 1);
for (int i = 0; i < numBits; ++i) {
encodeFlag((v & mask) != 0);
mask = mask >> 1;
}
}
public final void encodeLitInt(int v, int numBits) {
boolean neg = v < 0;
if (neg) { v = -v; }
encodeLitUInt(v, numBits);
encodeFlag(neg);
}
public final void encodeLitWithProbs(int v, int numBits, int[] probs) {
assert(v >= 0);
int mask = 1 << (numBits - 1);
for (int i = 0; i < numBits; ++i) {
encodeBoolean((v & mask) != 0, probs[i]);
mask = mask >> 1;
}
}
public final void encodeTree(int[] bits, int[] probs, int[] tree, int startBit, int startTree) {
// This is a similar tree structure used by the decoder
int pos = startTree;
for (int i = startBit; i < bits.length; ++i) {
boolean val = bits[i] == 1;
int prob = probs[pos >> 1];
pos = tree[pos+(val?1:0)];
encodeBoolean(val, prob);
}
}
/**
* Returns the data so far, note after this is called, the encoder can still be added to
* @return
*/
public ByteBuffer getData() {
ByteBuffer ret = ByteBuffer.allocate(pos + 2);
int bot = bottom << (8 - count);
if (bot >= 65536) {
addOneToOutput();
bot -= 65536;
}
ret.put(data, 0, pos);
ret.put((byte)((bot & 0xff00) >> 8));
ret.put((byte)(bot & 0xff));
ret.flip();
canEncode = false;
return ret;
}
}