use super::state_table::STATE_TABLE;
pub(crate) const MAX_CTX: usize = 65536;
const OUTPUT_BUFFER_SIZE: usize = 20 * 1024;
pub(crate) const INT_CTX_COUNT: usize = 13;
pub(crate) const INT_CTX_SIZE: usize = 512;
pub struct ArithEncoder {
pub(crate) c: u32,
pub(crate) a: u16,
pub(crate) ct: u8,
pub(crate) b: u8,
pub(crate) bp: i32,
pub(crate) output_chunks: Vec<Vec<u8>>,
pub(crate) outbuf: Vec<u8>,
pub(crate) context: Vec<u8>,
pub(crate) intctx: Vec<u8>,
pub(crate) iaidctx: Vec<u8>,
}
impl Default for ArithEncoder {
fn default() -> Self {
Self::new()
}
}
impl ArithEncoder {
pub fn new() -> Self {
Self {
c: 0,
a: 0x8000,
ct: 12,
b: 0,
bp: -1,
output_chunks: Vec::new(),
outbuf: Vec::with_capacity(OUTPUT_BUFFER_SIZE),
context: vec![0u8; MAX_CTX],
intctx: vec![0u8; INT_CTX_COUNT * INT_CTX_SIZE],
iaidctx: Vec::new(),
}
}
pub fn reset(&mut self) {
self.a = 0x8000;
self.c = 0;
self.ct = 12;
self.bp = -1;
self.b = 0;
self.context.fill(0);
self.intctx.fill(0);
self.iaidctx.clear();
}
pub fn flush(&mut self) {
self.outbuf.clear();
self.output_chunks.clear();
self.bp = -1;
}
pub fn encode_bit(&mut self, context: &mut [u8], ctx_num: u32, d: u8) {
encode_bit_raw(
&mut self.c,
&mut self.a,
&mut self.ct,
&mut self.b,
&mut self.bp,
&mut self.output_chunks,
&mut self.outbuf,
context,
ctx_num as usize,
d,
);
}
pub fn encode_final(&mut self) {
let tempc = self.c.wrapping_add(self.a as u32);
self.c |= 0xffff;
if self.c >= tempc {
self.c -= 0x8000;
}
self.c <<= self.ct;
byteout_raw(
&mut self.c,
&mut self.ct,
&mut self.b,
&mut self.bp,
&mut self.output_chunks,
&mut self.outbuf,
);
self.c <<= self.ct;
byteout_raw(
&mut self.c,
&mut self.ct,
&mut self.b,
&mut self.bp,
&mut self.output_chunks,
&mut self.outbuf,
);
emit_byte(
self.b,
&mut self.bp,
&mut self.output_chunks,
&mut self.outbuf,
);
if self.b != 0xff {
self.b = 0xff;
emit_byte(
self.b,
&mut self.bp,
&mut self.output_chunks,
&mut self.outbuf,
);
}
self.b = 0xac;
emit_byte(
self.b,
&mut self.bp,
&mut self.output_chunks,
&mut self.outbuf,
);
}
pub fn data_size(&self) -> usize {
OUTPUT_BUFFER_SIZE * self.output_chunks.len() + self.outbuf.len()
}
pub fn to_vec(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(self.data_size());
for chunk in &self.output_chunks {
buf.extend_from_slice(chunk);
}
buf.extend_from_slice(&self.outbuf);
buf
}
pub(crate) fn ensure_iaid_ctx(&mut self, symcodelen: u32) {
let needed = 1usize << symcodelen;
if self.iaidctx.len() < needed {
self.iaidctx.resize(needed, 0);
}
}
}
#[inline]
pub(crate) fn emit_byte(
byte: u8,
bp: &mut i32,
output_chunks: &mut Vec<Vec<u8>>,
outbuf: &mut Vec<u8>,
) {
if *bp < 0 {
return;
}
if outbuf.len() == OUTPUT_BUFFER_SIZE {
let full_chunk = std::mem::replace(outbuf, Vec::with_capacity(OUTPUT_BUFFER_SIZE));
output_chunks.push(full_chunk);
}
outbuf.push(byte);
}
#[inline]
pub(crate) fn byteout_raw(
c: &mut u32,
ct: &mut u8,
b: &mut u8,
bp: &mut i32,
output_chunks: &mut Vec<Vec<u8>>,
outbuf: &mut Vec<u8>,
) {
if *b == 0xff {
if *bp >= 0 {
if outbuf.len() == OUTPUT_BUFFER_SIZE {
let full_chunk = std::mem::replace(outbuf, Vec::with_capacity(OUTPUT_BUFFER_SIZE));
output_chunks.push(full_chunk);
}
outbuf.push(*b);
}
*b = (*c >> 20) as u8;
*bp += 1;
*c &= 0xf_ffff;
*ct = 7;
return;
}
if *c < 0x800_0000 {
if *bp >= 0 {
if outbuf.len() == OUTPUT_BUFFER_SIZE {
let full_chunk = std::mem::replace(outbuf, Vec::with_capacity(OUTPUT_BUFFER_SIZE));
output_chunks.push(full_chunk);
}
outbuf.push(*b);
}
*b = (*c >> 19) as u8;
*bp += 1;
*c &= 0x7_ffff;
*ct = 8;
return;
}
*b += 1;
if *b != 0xff {
if *bp >= 0 {
if outbuf.len() == OUTPUT_BUFFER_SIZE {
let full_chunk = std::mem::replace(outbuf, Vec::with_capacity(OUTPUT_BUFFER_SIZE));
output_chunks.push(full_chunk);
}
outbuf.push(*b);
}
*b = (*c >> 19) as u8;
*bp += 1;
*c &= 0x7_ffff;
*ct = 8;
return;
}
*c &= 0x7ff_ffff;
if *bp >= 0 {
if outbuf.len() == OUTPUT_BUFFER_SIZE {
let full_chunk = std::mem::replace(outbuf, Vec::with_capacity(OUTPUT_BUFFER_SIZE));
output_chunks.push(full_chunk);
}
outbuf.push(*b);
}
*b = (*c >> 20) as u8;
*bp += 1;
*c &= 0xf_ffff;
*ct = 7;
}
#[inline]
#[allow(clippy::too_many_arguments)]
pub(crate) fn encode_bit_raw(
c: &mut u32,
a: &mut u16,
ct: &mut u8,
b: &mut u8,
bp: &mut i32,
output_chunks: &mut Vec<Vec<u8>>,
outbuf: &mut Vec<u8>,
context: &mut [u8],
ctx_num: usize,
d: u8,
) {
let i = context[ctx_num];
let mps: u8 = if i > 46 { 1 } else { 0 };
let qe = STATE_TABLE[i as usize].qe;
if d != mps {
*a -= qe;
if *a < qe {
*c += qe as u32;
} else {
*a = qe;
}
context[ctx_num] = STATE_TABLE[i as usize].lps;
loop {
*a <<= 1;
*c <<= 1;
*ct -= 1;
if *ct == 0 {
byteout_raw(c, ct, b, bp, output_chunks, outbuf);
}
if *a & 0x8000 != 0 {
break;
}
}
} else {
*a -= qe;
if *a & 0x8000 == 0 {
if *a < qe {
*a = qe;
} else {
*c += qe as u32;
}
context[ctx_num] = STATE_TABLE[i as usize].mps;
loop {
*a <<= 1;
*c <<= 1;
*ct -= 1;
if *ct == 0 {
byteout_raw(c, ct, b, bp, output_chunks, outbuf);
}
if *a & 0x8000 != 0 {
break;
}
}
} else {
*c += qe as u32;
}
}
}