#![allow(dead_code)]
use crate::celt::entcode::{
EC_CODE_BITS, EC_CODE_BOT, EC_CODE_SHIFT, EC_CODE_TOP, EC_SYM_BITS, EC_SYM_MAX, EC_UINT_BITS,
EC_WINDOW_SIZE, EcCtx, EcWindow, celt_udiv, ec_ilog,
};
use crate::celt::types::{OpusInt32, OpusUint32};
use alloc::boxed::Box;
use alloc::vec;
use alloc::vec::Vec;
#[cfg(test)]
extern crate std;
#[derive(Debug)]
pub struct EcEnc<'a> {
ctx: EcCtx<'a>,
}
impl<'a> EcEnc<'a> {
#[must_use]
pub fn new(buf: &'a mut [u8]) -> Self {
let storage = buf.len() as OpusUint32;
let mut ctx = EcCtx::from_encoder_buffer(buf);
ctx.storage = storage;
ctx.end_offs = 0;
ctx.end_window = 0;
ctx.nend_bits = 0;
ctx.nbits_total = EC_CODE_BITS as OpusInt32 + 1;
ctx.offs = 0;
ctx.rng = EC_CODE_TOP;
ctx.rem = -1;
ctx.val = 0;
ctx.ext = 0;
ctx.error = 0;
Self { ctx }
}
#[must_use]
pub fn ctx(&self) -> &EcCtx<'a> {
&self.ctx
}
#[must_use]
pub fn ctx_mut(&mut self) -> &mut EcCtx<'a> {
&mut self.ctx
}
fn write_byte(&mut self, value: OpusUint32) -> OpusInt32 {
if self.ctx.offs + self.ctx.end_offs >= self.ctx.storage {
-1
} else {
let idx = self.ctx.offs as usize;
self.ctx.buffer_mut()[idx] = value as u8;
self.ctx.offs += 1;
0
}
}
fn write_byte_at_end(&mut self, value: OpusUint32) -> OpusInt32 {
if self.ctx.offs + self.ctx.end_offs >= self.ctx.storage {
-1
} else {
self.ctx.end_offs += 1;
let idx = (self.ctx.storage - self.ctx.end_offs) as usize;
self.ctx.buffer_mut()[idx] = value as u8;
0
}
}
fn carry_out(&mut self, c: OpusInt32) {
if c == EC_SYM_MAX as OpusInt32 {
self.ctx.ext = self.ctx.ext.wrapping_add(1);
} else {
let carry = c >> EC_SYM_BITS;
if self.ctx.rem >= 0 {
let value = (self.ctx.rem + carry) as OpusUint32;
self.ctx.error |= self.write_byte(value);
}
if self.ctx.ext > 0 {
let sym = (EC_SYM_MAX + carry as OpusUint32) & EC_SYM_MAX;
while self.ctx.ext > 0 {
self.ctx.error |= self.write_byte(sym);
self.ctx.ext -= 1;
}
}
self.ctx.rem = c & EC_SYM_MAX as OpusInt32;
}
}
fn normalize(&mut self) {
while self.ctx.rng <= EC_CODE_BOT {
self.carry_out((self.ctx.val >> EC_CODE_SHIFT) as OpusInt32);
self.ctx.val = (self.ctx.val << EC_SYM_BITS) & (EC_CODE_TOP - 1);
self.ctx.rng <<= EC_SYM_BITS;
self.ctx.nbits_total += EC_SYM_BITS as OpusInt32;
}
}
pub fn encode(&mut self, fl: OpusUint32, fh: OpusUint32, ft: OpusUint32) {
let r = celt_udiv(self.ctx.rng, ft);
if fl > 0 {
let diff = ft - fl;
self.ctx.val = self
.ctx
.val
.wrapping_add(self.ctx.rng.wrapping_sub(r.wrapping_mul(diff)));
self.ctx.rng = r.wrapping_mul(fh - fl);
} else {
self.ctx.rng = self.ctx.rng.wrapping_sub(r.wrapping_mul(ft - fh));
}
self.normalize();
}
pub fn encode_bin(&mut self, fl: OpusUint32, fh: OpusUint32, bits: u32) {
let r = self.ctx.rng >> bits;
let total = 1u32 << bits;
if fl > 0 {
self.ctx.val = self
.ctx
.val
.wrapping_add(self.ctx.rng.wrapping_sub(r.wrapping_mul(total - fl)));
self.ctx.rng = r.wrapping_mul(fh - fl);
} else {
self.ctx.rng = self.ctx.rng.wrapping_sub(r.wrapping_mul(total - fh));
}
self.normalize();
}
pub fn enc_bit_logp(&mut self, val: OpusInt32, logp: u32) {
let r = self.ctx.rng;
let l = self.ctx.val;
let s = r >> logp;
let r_minus_s = r - s;
if val != 0 {
self.ctx.val = l.wrapping_add(r_minus_s);
self.ctx.rng = s;
} else {
self.ctx.rng = r_minus_s;
}
self.normalize();
}
pub fn enc_icdf(&mut self, s: usize, icdf: &[u8], ftb: u32) {
let r = self.ctx.rng >> ftb;
if s > 0 {
let high = OpusUint32::from(icdf[s - 1]);
self.ctx.val = self
.ctx
.val
.wrapping_add(self.ctx.rng.wrapping_sub(r.wrapping_mul(high)));
self.ctx.rng = r.wrapping_mul(high - OpusUint32::from(icdf[s]));
} else {
self.ctx.rng = self
.ctx
.rng
.wrapping_sub(r.wrapping_mul(OpusUint32::from(icdf[s])));
}
self.normalize();
}
pub fn enc_icdf16(&mut self, s: usize, icdf: &[u16], ftb: u32) {
let r = self.ctx.rng >> ftb;
if s > 0 {
let high = OpusUint32::from(icdf[s - 1]);
self.ctx.val = self
.ctx
.val
.wrapping_add(self.ctx.rng.wrapping_sub(r.wrapping_mul(high)));
self.ctx.rng = r.wrapping_mul(high - OpusUint32::from(icdf[s]));
} else {
self.ctx.rng = self
.ctx
.rng
.wrapping_sub(r.wrapping_mul(OpusUint32::from(icdf[s])));
}
self.normalize();
}
pub fn enc_uint(&mut self, fl: OpusUint32, ft: OpusUint32) {
assert!(ft > 1);
let ft = ft - 1;
let mut ftb = (32 - ft.leading_zeros()) as OpusInt32;
if ftb as usize > EC_UINT_BITS {
ftb -= EC_UINT_BITS as OpusInt32;
let ft_small = (ft >> ftb) + 1;
let fl_small = fl >> ftb;
self.encode(fl_small, fl_small + 1, ft_small);
let mask = (1u32 << ftb) - 1;
self.enc_bits(fl & mask, ftb as u32);
} else {
self.encode(fl, fl + 1, ft + 1);
}
}
pub fn enc_bits(&mut self, fl: OpusUint32, bits: u32) {
debug_assert!(bits > 0);
let mut window = self.ctx.end_window;
let mut used = self.ctx.nend_bits;
if used as u32 + bits > EC_WINDOW_SIZE as u32 {
while used >= EC_SYM_BITS as OpusInt32 {
self.ctx.error |=
self.write_byte_at_end((window & EC_SYM_MAX as EcWindow) as OpusUint32);
window >>= EC_SYM_BITS;
used -= EC_SYM_BITS as OpusInt32;
}
}
window |= (fl as EcWindow) << (used as u32);
used += bits as OpusInt32;
self.ctx.end_window = window;
self.ctx.nend_bits = used;
self.ctx.nbits_total += bits as OpusInt32;
}
pub fn enc_patch_initial_bits(&mut self, val: OpusUint32, nbits: u32) {
assert!(nbits <= EC_SYM_BITS);
let shift = EC_SYM_BITS - nbits;
let mask = ((1u32 << nbits) - 1) << shift;
let val_masked = (val & ((1u32 << nbits) - 1)) << shift;
if self.ctx.offs > 0 {
let buffer = self.ctx.buffer_mut();
let byte = OpusUint32::from(buffer[0]);
buffer[0] = ((byte & !mask) | val_masked) as u8;
} else if self.ctx.rem >= 0 {
let rem = self.ctx.rem as OpusUint32;
self.ctx.rem = ((rem & !mask) | val_masked) as OpusInt32;
} else if self.ctx.rng <= (EC_CODE_TOP >> nbits) {
let mask_shifted = (mask as OpusUint32) << EC_CODE_SHIFT;
self.ctx.val =
(self.ctx.val & !mask_shifted) | ((val_masked as OpusUint32) << EC_CODE_SHIFT);
} else {
self.ctx.error = -1;
}
}
pub fn enc_shrink(&mut self, size: OpusUint32) {
assert!(self.ctx.offs + self.ctx.end_offs <= size);
if size < self.ctx.storage {
let len = self.ctx.end_offs as usize;
if len > 0 {
let src_start = (self.ctx.storage - self.ctx.end_offs) as usize;
let dst_start = (size - self.ctx.end_offs) as usize;
self.ctx
.buffer_mut()
.copy_within(src_start..src_start + len, dst_start);
}
self.ctx.storage = size;
}
}
pub fn enc_done(&mut self) {
#[cfg(test)]
let trace_frame = enc_done_trace::frame_to_dump();
#[cfg(test)]
if let Some(frame_idx) = trace_frame {
crate::test_trace::trace_println!(
"opus_enc_done[{frame_idx}].init.rng=0x{:08x}",
self.ctx.rng
);
crate::test_trace::trace_println!(
"opus_enc_done[{frame_idx}].init.val=0x{:08x}",
self.ctx.val
);
crate::test_trace::trace_println!(
"opus_enc_done[{frame_idx}].init.rem={}",
self.ctx.rem
);
crate::test_trace::trace_println!(
"opus_enc_done[{frame_idx}].init.ext={}",
self.ctx.ext
);
crate::test_trace::trace_println!(
"opus_enc_done[{frame_idx}].init.end_window=0x{:08x}",
self.ctx.end_window
);
crate::test_trace::trace_println!(
"opus_enc_done[{frame_idx}].init.nend_bits={}",
self.ctx.nend_bits
);
crate::test_trace::trace_println!(
"opus_enc_done[{frame_idx}].init.offs={}",
self.ctx.offs
);
crate::test_trace::trace_println!(
"opus_enc_done[{frame_idx}].init.end_offs={}",
self.ctx.end_offs
);
crate::test_trace::trace_println!(
"opus_enc_done[{frame_idx}].init.storage={}",
self.ctx.storage
);
}
let mut window = self.ctx.end_window;
let mut used = self.ctx.nend_bits;
let mut l: OpusInt32 = EC_CODE_BITS as OpusInt32 - ec_ilog(self.ctx.rng);
let mut msk = (EC_CODE_TOP - 1) >> l;
let mut end = (self.ctx.val + msk) & !msk;
#[cfg(test)]
if let Some(frame_idx) = trace_frame {
crate::test_trace::trace_println!("opus_enc_done[{frame_idx}].l_init={}", l);
crate::test_trace::trace_println!("opus_enc_done[{frame_idx}].msk_init=0x{:08x}", msk);
crate::test_trace::trace_println!("opus_enc_done[{frame_idx}].end_init=0x{:08x}", end);
}
if (end | msk) >= self.ctx.val.wrapping_add(self.ctx.rng) {
l += 1;
msk >>= 1;
end = (self.ctx.val + msk) & !msk;
}
#[cfg(test)]
if let Some(frame_idx) = trace_frame {
crate::test_trace::trace_println!("opus_enc_done[{frame_idx}].l_adj={}", l);
crate::test_trace::trace_println!("opus_enc_done[{frame_idx}].msk_adj=0x{:08x}", msk);
crate::test_trace::trace_println!("opus_enc_done[{frame_idx}].end_adj=0x{:08x}", end);
}
#[cfg(test)]
let mut carry_iter: OpusInt32 = 0;
while l > 0 {
#[cfg(test)]
if let Some(frame_idx) = trace_frame {
crate::test_trace::trace_println!(
"opus_enc_done[{frame_idx}].carry[{carry_iter}].in=0x{:08x}",
end >> EC_CODE_SHIFT
);
}
self.carry_out((end >> EC_CODE_SHIFT) as OpusInt32);
end = (end << EC_SYM_BITS) & (EC_CODE_TOP - 1);
l -= EC_SYM_BITS as OpusInt32;
#[cfg(test)]
{
carry_iter += 1;
}
}
if self.ctx.rem >= 0 || self.ctx.ext > 0 {
self.carry_out(0);
}
#[cfg(test)]
if let Some(frame_idx) = trace_frame {
crate::test_trace::trace_println!(
"opus_enc_done[{frame_idx}].after_carry.offs={}",
self.ctx.offs
);
crate::test_trace::trace_println!(
"opus_enc_done[{frame_idx}].after_carry.rem={}",
self.ctx.rem
);
crate::test_trace::trace_println!(
"opus_enc_done[{frame_idx}].after_carry.ext={}",
self.ctx.ext
);
}
while used >= EC_SYM_BITS as OpusInt32 {
self.ctx.error |=
self.write_byte_at_end((window & EC_SYM_MAX as EcWindow) as OpusUint32);
window >>= EC_SYM_BITS;
used -= EC_SYM_BITS as OpusInt32;
}
#[cfg(test)]
if let Some(frame_idx) = trace_frame {
crate::test_trace::trace_println!(
"opus_enc_done[{frame_idx}].after_window.end_offs={}",
self.ctx.end_offs
);
crate::test_trace::trace_println!(
"opus_enc_done[{frame_idx}].after_window.used={}",
used
);
crate::test_trace::trace_println!(
"opus_enc_done[{frame_idx}].after_window.window=0x{:08x}",
window
);
}
if self.ctx.error == 0 {
let start = self.ctx.offs as usize;
let end_idx = (self.ctx.storage - self.ctx.end_offs) as usize;
self.ctx.buffer_mut()[start..end_idx].fill(0);
if used > 0 {
if self.ctx.end_offs >= self.ctx.storage {
self.ctx.error = -1;
} else {
let l_remaining = -l;
if self.ctx.offs + self.ctx.end_offs >= self.ctx.storage && l_remaining < used {
if l_remaining > 0 {
window &= ((1u32 << l_remaining as u32) - 1) as EcWindow;
} else {
window = 0;
}
self.ctx.error = -1;
}
let idx = (self.ctx.storage - self.ctx.end_offs - 1) as usize;
self.ctx.buffer_mut()[idx] |= window as u8;
}
}
}
self.ctx.end_window = window;
self.ctx.nend_bits = used;
#[cfg(test)]
if let Some(frame_idx) = trace_frame {
crate::test_trace::trace_println!(
"opus_enc_done[{frame_idx}].final.offs={}",
self.ctx.offs
);
crate::test_trace::trace_println!(
"opus_enc_done[{frame_idx}].final.end_offs={}",
self.ctx.end_offs
);
crate::test_trace::trace_println!(
"opus_enc_done[{frame_idx}].final.error={}",
self.ctx.error
);
}
}
#[must_use]
pub fn range_bytes(&self) -> OpusUint32 {
self.ctx.range_bytes()
}
}
impl EcEnc<'static> {
#[must_use]
pub(crate) fn from_owned_buffer(buf: Box<[u8]>) -> Self {
let storage = buf.len() as OpusUint32;
let mut ctx = EcCtx::from_owned_buffer(buf);
ctx.storage = storage;
ctx.end_offs = 0;
ctx.end_window = 0;
ctx.nend_bits = 0;
ctx.nbits_total = EC_CODE_BITS as OpusInt32 + 1;
ctx.offs = 0;
ctx.rng = EC_CODE_TOP;
ctx.rem = -1;
ctx.val = 0;
ctx.ext = 0;
ctx.error = 0;
Self { ctx }
}
#[must_use]
pub(crate) fn with_capacity(capacity: usize) -> Self {
Self::from_owned_buffer(vec![0u8; capacity].into_boxed_slice())
}
}
#[derive(Clone)]
pub struct EcEncSnapshot {
storage: OpusUint32,
end_offs: OpusUint32,
end_window: EcWindow,
nend_bits: OpusInt32,
nbits_total: OpusInt32,
offs: OpusUint32,
rng: OpusUint32,
val: OpusUint32,
ext: OpusUint32,
rem: OpusInt32,
error: OpusInt32,
buffer: Vec<u8>,
}
impl EcEncSnapshot {
#[must_use]
pub fn capture(enc: &EcEnc<'_>) -> Self {
let ctx = enc.ctx();
Self {
storage: ctx.storage,
end_offs: ctx.end_offs,
end_window: ctx.end_window,
nend_bits: ctx.nend_bits,
nbits_total: ctx.nbits_total,
offs: ctx.offs,
rng: ctx.rng,
val: ctx.val,
ext: ctx.ext,
rem: ctx.rem,
error: ctx.error,
buffer: ctx.buffer().to_vec(),
}
}
pub fn restore(&self, enc: &mut EcEnc<'_>) {
let ctx = enc.ctx_mut();
assert_eq!(self.buffer.len(), ctx.buffer().len());
ctx.storage = self.storage;
ctx.end_offs = self.end_offs;
ctx.end_window = self.end_window;
ctx.nend_bits = self.nend_bits;
ctx.nbits_total = self.nbits_total;
ctx.offs = self.offs;
ctx.rng = self.rng;
ctx.val = self.val;
ctx.ext = self.ext;
ctx.rem = self.rem;
ctx.error = self.error;
ctx.buffer_mut().copy_from_slice(&self.buffer);
}
#[must_use]
pub(crate) fn buffer_len(&self) -> usize {
self.buffer.len()
}
}
impl<'a> core::ops::Deref for EcEnc<'a> {
type Target = EcCtx<'a>;
fn deref(&self) -> &Self::Target {
&self.ctx
}
}
impl<'a> core::ops::DerefMut for EcEnc<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.ctx
}
}
#[cfg(test)]
mod enc_done_trace {
extern crate std;
use core::sync::atomic::{AtomicIsize, Ordering};
use std::env;
use std::sync::OnceLock;
pub(crate) struct TraceConfig {
frame: Option<usize>,
}
static TRACE_CONFIG: OnceLock<Option<TraceConfig>> = OnceLock::new();
static CURRENT_FRAME: AtomicIsize = AtomicIsize::new(-1);
pub(crate) fn set_frame(frame_idx: usize) {
CURRENT_FRAME.store(frame_idx as isize, Ordering::Relaxed);
}
fn current_frame() -> Option<usize> {
let value = CURRENT_FRAME.load(Ordering::Relaxed);
if value >= 0 {
Some(value as usize)
} else {
None
}
}
fn config() -> Option<&'static TraceConfig> {
TRACE_CONFIG
.get_or_init(|| {
let enabled = match env::var("OPUS_TRACE_RANGE_DONE_DETAIL") {
Ok(value) => !value.is_empty() && value != "0",
Err(_) => false,
};
if !enabled {
return None;
}
let frame = env::var("OPUS_TRACE_RANGE_DONE_DETAIL_FRAME")
.ok()
.and_then(|value| value.parse::<usize>().ok());
Some(TraceConfig { frame })
})
.as_ref()
}
pub(crate) fn frame_to_dump() -> Option<usize> {
let cfg = config()?;
let frame_idx = current_frame()?;
if cfg.frame.map_or(true, |frame| frame == frame_idx) {
Some(frame_idx)
} else {
None
}
}
}
#[cfg(test)]
pub(crate) fn set_enc_done_trace_frame(frame_idx: usize) {
enc_done_trace::set_frame(frame_idx);
}
#[cfg(test)]
mod tests {
use alloc::vec;
use alloc::vec::Vec;
use super::EcEnc;
use crate::celt::entcode::{EC_CODE_BITS, EC_CODE_TOP, EC_WINDOW_SIZE};
use crate::celt::entdec::EcDec;
#[test]
fn encoder_initialises_like_reference() {
let mut buf = vec![0u8; 8];
let enc = EcEnc::new(&mut buf);
assert_eq!(enc.storage, 8);
assert_eq!(enc.end_offs, 0);
assert_eq!(enc.end_window, 0);
assert_eq!(enc.nend_bits, 0);
assert_eq!(enc.nbits_total, EC_CODE_BITS as i32 + 1);
assert_eq!(enc.offs, 0);
assert_eq!(enc.rng, EC_CODE_TOP);
assert_eq!(enc.rem, -1);
assert_eq!(enc.val, 0);
assert_eq!(enc.ext, 0);
assert_eq!(enc.error, 0);
}
#[test]
fn enc_bits_appends_to_tail() {
let mut buf = vec![0u8; 4];
let mut enc = EcEnc::new(&mut buf);
enc.enc_bits(0b1010, 4);
assert_eq!(enc.end_window, 0b1010);
assert_eq!(enc.nend_bits, 4);
assert_eq!(enc.nbits_total, EC_CODE_BITS as i32 + 1 + 4);
}
#[test]
fn patch_initial_bits_updates_finalised_byte() {
let mut buf = vec![0u8; 2];
let mut enc = EcEnc::new(&mut buf);
enc.offs = 1;
enc.buffer_mut()[0] = 0b1111_0000;
enc.enc_patch_initial_bits(0b1010, 4);
assert_eq!(enc.buffer()[0], 0b1010_0000);
}
#[test]
fn enc_shrink_moves_tail_bits() {
let mut buf = vec![0u8; 4];
let mut enc = EcEnc::new(&mut buf);
enc.end_offs = 1;
enc.storage = 4;
enc.buffer_mut()[3] = 0xAA;
enc.enc_shrink(3);
assert_eq!(enc.storage, 3);
assert_eq!(enc.buffer()[2], 0xAA);
}
#[test]
fn enc_done_flushes_raw_bits() {
let mut buf = vec![0u8; 4];
let mut enc = EcEnc::new(&mut buf);
enc.enc_bits(0b1011, 4);
enc.enc_done();
assert_eq!(enc.buffer()[3], 0b1011);
assert_eq!(enc.error, 0);
assert!(enc.nend_bits < EC_WINDOW_SIZE as i32);
}
#[test]
fn entropy_uint_roundtrip() {
const DATA_SIZE: usize = 1_000_000;
let mut buf = vec![0u8; DATA_SIZE];
{
let mut enc = EcEnc::new(&mut buf);
for ft in 2..1024u32 {
for i in 0..ft {
enc.enc_uint(i, ft);
}
}
enc.enc_done();
assert_eq!(enc.error, 0, "encoding error");
}
{
let mut dec = EcDec::new(&mut buf);
for ft in 2..1024u32 {
for i in 0..ft {
let sym = dec.dec_uint(ft);
assert_eq!(sym, i, "decoded {} instead of {} with ft of {}", sym, i, ft);
}
}
}
}
#[test]
fn entropy_raw_bits_roundtrip() {
use crate::celt::entcode::ec_tell;
const DATA_SIZE: usize = 1_000_000;
let mut buf = vec![0u8; DATA_SIZE];
{
let mut enc = EcEnc::new(&mut buf);
for ftb in 1..16u32 {
for i in 0..(1u32 << ftb) {
let nbits_before = ec_tell(&enc);
enc.enc_bits(i, ftb);
let nbits_after = ec_tell(&enc);
assert_eq!(
nbits_after - nbits_before,
ftb as i32,
"used {} bits to encode {} bits directly",
nbits_after - nbits_before,
ftb
);
}
}
enc.enc_done();
assert_eq!(enc.error, 0);
}
{
let mut dec = EcDec::new(&mut buf);
for ftb in 1..16u32 {
for i in 0..(1u32 << ftb) {
let sym = dec.dec_bits(ftb);
assert_eq!(
sym, i,
"decoded {} instead of {} with ftb of {}",
sym, i, ftb
);
}
}
}
}
#[test]
fn entropy_patch_initial_bits() {
const DATA_SIZE: usize = 10_000;
let mut buf = vec![0u8; DATA_SIZE];
{
let mut enc = EcEnc::new(&mut buf);
enc.enc_bit_logp(0, 1);
enc.enc_bit_logp(0, 1);
enc.enc_bit_logp(0, 1);
enc.enc_bit_logp(0, 1);
enc.enc_bit_logp(0, 2);
enc.enc_patch_initial_bits(3, 2);
assert_eq!(enc.error, 0, "patch_initial_bits failed");
enc.enc_patch_initial_bits(0, 5);
assert_ne!(
enc.error, 0,
"patch_initial_bits didn't fail when it should have"
);
enc.enc_done();
assert_eq!(enc.range_bytes(), 1);
assert_eq!(buf[0], 192);
}
{
let mut enc = EcEnc::new(&mut buf);
enc.enc_bit_logp(0, 1);
enc.enc_bit_logp(0, 1);
enc.enc_bit_logp(1, 6);
enc.enc_bit_logp(0, 2);
enc.enc_patch_initial_bits(0, 2);
assert_eq!(enc.error, 0, "patch_initial_bits failed");
enc.enc_done();
assert_eq!(enc.range_bytes(), 2);
assert_eq!(buf[0], 63);
}
}
#[test]
fn entropy_raw_bits_overfill() {
let mut buf = vec![0u8; 2];
{
let mut enc = EcEnc::new(&mut buf);
enc.enc_bit_logp(0, 2);
for _ in 0..48 {
enc.enc_bits(0, 1);
}
enc.enc_done();
assert_ne!(
enc.error, 0,
"raw bits overfill didn't fail when it should have"
);
}
{
let mut enc = EcEnc::new(&mut buf);
for _ in 0..17 {
enc.enc_bits(0, 1);
}
enc.enc_done();
assert_ne!(enc.error, 0, "17 raw bits encoded in two bytes");
}
}
struct Lcg(u32);
impl Lcg {
fn new(seed: u32) -> Self {
Self(seed)
}
fn next(&mut self) -> u32 {
self.0 = self.0.wrapping_mul(1_103_515_245).wrapping_add(12_345);
self.0
}
}
#[test]
fn entropy_random_uint_streams() {
use crate::celt::entcode::ec_tell_frac;
const DATA_SIZE: usize = 10_000;
const NUM_ITERATIONS: usize = 1_000; const SEED: u32 = 12345;
let mut buf = vec![0u8; DATA_SIZE];
let mut rng = Lcg::new(SEED);
for iteration in 0..NUM_ITERATIONS {
let shift1 = (rng.next() % 11) as u32;
let divisor = u32::MAX.wrapping_shr(shift1).saturating_add(1);
let ft = rng.next().wrapping_div(divisor.max(1)).saturating_add(10);
let shift2 = (rng.next() % 9) as u32;
let divisor2 = u32::MAX.wrapping_shr(shift2).saturating_add(1);
let sz = (rng.next().wrapping_div(divisor2.max(1))) as usize;
let zeros = (rng.next() % 13) == 0;
let data: Vec<u32> = (0..sz)
.map(|_| if zeros { 0 } else { rng.next() % ft })
.collect();
let mut enc = EcEnc::new(&mut buf);
let mut tells: Vec<u32> = Vec::with_capacity(sz + 1);
tells.push(ec_tell_frac(&enc));
for &value in &data {
enc.enc_uint(value, ft);
tells.push(ec_tell_frac(&enc));
}
if rng.next() % 2 == 0 {
use crate::celt::entcode::ec_tell;
while ec_tell(&enc) % 8 != 0 {
enc.enc_uint(rng.next() % 2, 2);
}
}
enc.enc_done();
assert_eq!(enc.error, 0, "encoding error at iteration {}", iteration);
let mut dec = EcDec::new(&mut buf);
assert_eq!(
ec_tell_frac(&dec),
tells[0],
"tell mismatch at symbol 0, iteration {}",
iteration
);
for (j, &expected) in data.iter().enumerate() {
let sym = dec.dec_uint(ft);
assert_eq!(
sym, expected,
"decoded {} instead of {} with ft of {} at position {} of {} (iteration {})",
sym, expected, ft, j, sz, iteration
);
assert_eq!(
ec_tell_frac(&dec),
tells[j + 1],
"tell mismatch at symbol {}, iteration {}",
j + 1,
iteration
);
}
}
}
#[test]
fn entropy_method_compatibility() {
const DATA_SIZE: usize = 10_000;
const NUM_ITERATIONS: usize = 1_000; const SEED: u32 = 54321;
let mut buf = vec![0u8; DATA_SIZE];
let mut rng = Lcg::new(SEED);
for iteration in 0..NUM_ITERATIONS {
let shift = (rng.next() % 9) as u32;
let divisor = u32::MAX.wrapping_shr(shift).saturating_add(1);
let sz = rng.next().wrapping_div(divisor.max(1)) as usize;
let sz = sz.min(500);
let divisor_half = u32::MAX.wrapping_shr(1).saturating_add(1);
let data: Vec<u32> = (0..sz)
.map(|_| rng.next().wrapping_div(divisor_half.max(1)))
.collect();
let logp1: Vec<u32> = (0..sz).map(|_| (rng.next() % 15) + 1).collect();
let enc_method: Vec<u32> = (0..sz)
.map(|_| {
let divisor4 = u32::MAX.wrapping_shr(2).saturating_add(1);
rng.next().wrapping_div(divisor4.max(1))
})
.collect();
let mut enc = EcEnc::new(&mut buf);
for j in 0..sz {
let d = data[j];
let logp = logp1[j];
let ft = 1u32 << logp;
match enc_method[j] % 4 {
0 => {
let (fl, fh) = if d != 0 { (ft - 1, ft) } else { (0, 1) };
enc.encode(fl, fh, ft);
}
1 => {
let (fl, fh) = if d != 0 { (ft - 1, ft) } else { (0, 1) };
enc.encode_bin(fl, fh, logp);
}
2 => {
enc.enc_bit_logp(d as i32, logp);
}
_ => {
let icdf = [1u8, 0];
enc.enc_icdf(d as usize, &icdf, logp);
}
}
}
enc.enc_done();
assert_eq!(enc.error, 0, "encoding error at iteration {}", iteration);
let mut dec = EcDec::new(&mut buf);
for j in 0..sz {
let expected = data[j];
let logp = logp1[j];
let ft = 1u32 << logp;
let sym = match enc_method[j] % 4 {
0 => {
let fs = dec.decode(ft);
let s = if fs >= ft - 1 { 1 } else { 0 };
let (fl, fh) = if s != 0 { (ft - 1, ft) } else { (0, 1) };
dec.update(fl, fh, ft);
s
}
1 => {
let fs = dec.decode_bin(logp);
let s = if fs >= ft - 1 { 1 } else { 0 };
let (fl, fh) = if s != 0 { (ft - 1, ft) } else { (0, 1) };
dec.update(fl, fh, ft);
s
}
2 => {
dec.dec_bit_logp(logp) as u32
}
_ => {
let icdf = [1u8, 0];
dec.dec_icdf(&icdf, logp) as u32
}
};
assert_eq!(
sym, expected,
"decoded {} instead of {} at position {} of {} (iteration {})",
sym, expected, j, sz, iteration
);
}
}
}
}