#![allow(dead_code)]
use alloc::boxed::Box;
use crate::celt::types::{OpusInt32, OpusUint32};
pub type EcWindow = OpusUint32;
pub const EC_WINDOW_SIZE: usize = core::mem::size_of::<EcWindow>() * 8;
pub const EC_SYM_BITS: u32 = 8;
pub const EC_CODE_BITS: u32 = 32;
pub const EC_SYM_MAX: OpusUint32 = (1u32 << EC_SYM_BITS) - 1;
pub const EC_CODE_TOP: OpusUint32 = 1u32 << (EC_CODE_BITS - 1);
pub const EC_CODE_BOT: OpusUint32 = EC_CODE_TOP >> EC_SYM_BITS;
pub const EC_CODE_SHIFT: u32 = EC_CODE_BITS - EC_SYM_BITS - 1;
pub const EC_CODE_EXTRA: u32 = ((EC_CODE_BITS - 2) % EC_SYM_BITS) + 1;
pub const EC_UINT_BITS: usize = 8;
pub const BITRES: u32 = 3;
#[derive(Debug)]
pub(crate) enum EcBuffer<'a> {
Borrowed(&'a [u8]),
BorrowedMut(&'a mut [u8]),
Owned(Box<[u8]>),
}
impl EcBuffer<'_> {
fn len(&self) -> usize {
match self {
Self::Borrowed(buf) => buf.len(),
Self::BorrowedMut(buf) => buf.len(),
Self::Owned(buf) => buf.len(),
}
}
fn as_slice(&self) -> &[u8] {
match self {
Self::Borrowed(buf) => buf,
Self::BorrowedMut(buf) => buf,
Self::Owned(buf) => buf,
}
}
fn as_mut_slice(&mut self) -> &mut [u8] {
match self {
Self::Borrowed(_) => panic!("attempted mutable access to read-only entropy buffer"),
Self::BorrowedMut(buf) => buf,
Self::Owned(buf) => buf,
}
}
}
#[derive(Debug)]
pub struct EcCtx<'a> {
pub(crate) buf: EcBuffer<'a>,
pub storage: OpusUint32,
pub end_offs: OpusUint32,
pub end_window: EcWindow,
pub nend_bits: OpusInt32,
pub nbits_total: OpusInt32,
pub offs: OpusUint32,
pub rng: OpusUint32,
pub val: OpusUint32,
pub ext: OpusUint32,
pub rem: OpusInt32,
pub error: OpusInt32,
}
impl<'a> EcCtx<'a> {
fn with_buffer(buf: EcBuffer<'a>) -> Self {
let storage = buf.len() as OpusUint32;
Self {
buf,
storage,
end_offs: 0,
end_window: 0,
nend_bits: 0,
nbits_total: 0,
offs: 0,
rng: 1,
val: 0,
ext: 0,
rem: 0,
error: 0,
}
}
#[must_use]
pub fn from_encoder_buffer(buf: &'a mut [u8]) -> Self {
Self::with_buffer(EcBuffer::BorrowedMut(buf))
}
#[must_use]
pub fn from_decoder_buffer(buf: &'a [u8]) -> Self {
Self::with_buffer(EcBuffer::Borrowed(buf))
}
#[must_use]
pub(crate) fn from_owned_buffer(buf: Box<[u8]>) -> Self {
Self::with_buffer(EcBuffer::Owned(buf))
}
#[must_use]
pub fn range_bytes(&self) -> OpusUint32 {
self.offs
}
#[must_use]
pub fn buffer(&self) -> &[u8] {
self.buf.as_slice()
}
#[must_use]
pub fn buffer_mut(&mut self) -> &mut [u8] {
self.buf.as_mut_slice()
}
#[must_use]
pub fn error(&self) -> OpusInt32 {
self.error
}
}
#[must_use]
pub fn ec_ilog(mut v: OpusUint32) -> OpusInt32 {
let mut ret = OpusInt32::from(v != 0);
let mut m = OpusInt32::from((v & 0xFFFF_0000) != 0) << 4;
v >>= m;
ret |= m;
m = OpusInt32::from((v & 0xFF00) != 0) << 3;
v >>= m;
ret |= m;
m = OpusInt32::from((v & 0xF0) != 0) << 2;
v >>= m;
ret |= m;
m = OpusInt32::from((v & 0xC) != 0) << 1;
v >>= m;
ret |= m;
ret + OpusInt32::from((v & 0x2) != 0)
}
#[must_use]
pub fn ec_tell(ctx: &EcCtx<'_>) -> OpusInt32 {
ctx.nbits_total - ec_ilog(ctx.rng)
}
#[must_use]
pub fn ec_tell_frac(ctx: &EcCtx<'_>) -> OpusUint32 {
const CORRECTION: [u32; 8] = [35733, 38967, 42495, 46340, 50535, 55109, 60097, 65535];
let nbits = (ctx.nbits_total as OpusUint32) << BITRES;
let mut l = ec_ilog(ctx.rng);
debug_assert!(l >= 16);
let r = ctx.rng >> ((l - 16) as u32);
let b = (r >> 12) as i32 - 8;
debug_assert!((0..CORRECTION.len() as i32).contains(&b));
let mut b = b as usize;
if r > CORRECTION[b] {
b += 1;
}
l = (l << 3) + b as OpusInt32;
nbits.wrapping_sub(l as OpusUint32)
}
#[allow(clippy::unreadable_literal)]
pub const SMALL_DIV_TABLE: [OpusUint32; 128] = [
0xFFFF_FFFF,
0x5555_5555,
0x3333_3333,
0x2492_4924,
0x1C71_C71C,
0x1745_D174,
0x13B1_3B13,
0x1111_1111,
0x0F0F_0F0F,
0x0D79_435E,
0x0C30_C30C,
0x0B21_642C,
0x0A3D_70A3,
0x097B_425E,
0x08D3_DCB0,
0x0842_1084,
0x07C1_F07C,
0x0750_7507,
0x06EB_3E45,
0x0690_6906,
0x063E_7063,
0x05F4_17D0,
0x05B0_5B05,
0x0572_620A,
0x0539_7829,
0x0505_0505,
0x04D4_873E,
0x04A7_904A,
0x047D_C11F,
0x0456_C797,
0x0432_5C53,
0x0410_4104,
0x03F0_3F03,
0x03D2_2635,
0x03B5_CC0E,
0x039B_0AD1,
0x0381_C0E0,
0x0369_D036,
0x0353_1DEC,
0x033D_91D2,
0x0329_161F,
0x0315_9721,
0x0303_0303,
0x02F1_4990,
0x02E0_5C0B,
0x02D0_2D02,
0x02C0_B02C,
0x02B1_DA46,
0x02A3_A0FD,
0x0295_FAD4,
0x0288_DF0C,
0x027C_4597,
0x0270_2702,
0x0264_7C69,
0x0259_3F69,
0x024E_6A17,
0x0243_F6F0,
0x0239_E0D5,
0x0230_2302,
0x0226_B902,
0x021D_9EAD,
0x0214_D021,
0x020C_49BA,
0x0204_0810,
0x01FC_07F0,
0x01F4_4659,
0x01EC_C07B,
0x01E5_73AC,
0x01DE_5D6E,
0x01D7_7B65,
0x01D0_CB58,
0x01CA_4B30,
0x01C3_F8F0,
0x01BD_D2B8,
0x01B7_D6C3,
0x01B2_0364,
0x01AC_5701,
0x01A6_D01A,
0x01A1_6D3F,
0x019C_2D14,
0x0197_0E4F,
0x0192_0FB4,
0x018D_3018,
0x0188_6E5F,
0x0183_C977,
0x017F_405F,
0x017A_D220,
0x0176_7DCE,
0x0172_4287,
0x016E_1F76,
0x016A_13CD,
0x0166_1EC6,
0x0162_3FA7,
0x015E_75BB,
0x015A_C056,
0x0157_1ED3,
0x0153_9094,
0x0150_1501,
0x014C_AB88,
0x0149_539E,
0x0146_0CBC,
0x0142_D662,
0x013F_B013,
0x013C_995A,
0x0139_91C2,
0x0136_98DF,
0x0133_AE45,
0x0130_D190,
0x012E_025C,
0x012B_404A,
0x0128_8B01,
0x0125_E227,
0x0123_4567,
0x0120_B470,
0x011E_2EF3,
0x011B_B4A4,
0x0119_4538,
0x0116_E068,
0x0114_85F0,
0x0112_358E,
0x010F_EF01,
0x010D_B20A,
0x010B_7E6E,
0x0109_53F3,
0x0107_3260,
0x0105_197F,
0x0103_091B,
0x0101_0101,
];
#[must_use]
pub fn celt_udiv(n: OpusUint32, d: OpusUint32) -> OpusUint32 {
debug_assert!(d > 0);
if d > 256 {
n / d
} else {
let t = ec_ilog(d & d.wrapping_neg()) as u32;
debug_assert!(t >= 1);
let q = (u64::from(SMALL_DIV_TABLE[(d >> t) as usize]) * u64::from(n >> (t - 1))) >> 32;
let q = q as OpusUint32;
q + OpusUint32::from(n.wrapping_sub(q * d) >= d)
}
}
#[must_use]
pub fn celt_sudiv(n: OpusInt32, d: OpusInt32) -> OpusInt32 {
debug_assert!(d > 0);
if n < 0 {
-(celt_udiv(n.unsigned_abs(), d as OpusUint32) as OpusInt32)
} else {
celt_udiv(n as OpusUint32, d as OpusUint32) as OpusInt32
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ec_ilog_matches_reference_values() {
let expected = [
(0u32, 0),
(1, 1),
(2, 2),
(3, 2),
(4, 3),
(7, 3),
(8, 4),
(15, 4),
(16, 5),
(31, 5),
(32, 6),
(255, 8),
(256, 9),
(1023, 10),
(1024, 11),
];
for (input, output) in expected {
assert_eq!(ec_ilog(input), output, "ec_ilog({input})");
}
}
fn slow_tell_frac(ctx: &EcCtx<'_>) -> OpusUint32 {
let mut l = ec_ilog(ctx.rng);
debug_assert!(l >= 16);
let mut r = ctx.rng >> ((l - 16) as u32);
for _ in 0..BITRES as usize {
r = (r * r) >> 15;
let b = (r >> 16) as i32;
l = (l << 1) | b;
r >>= b;
}
((ctx.nbits_total as OpusUint32) << BITRES).wrapping_sub(l as OpusUint32)
}
#[test]
fn fast_tell_frac_matches_reference() {
let mut scratch = [0u8; 1];
let mut ctx = EcCtx::from_encoder_buffer(&mut scratch);
let samples = [
(0x8000u32, 0),
(0xFFFFu32, 5),
(0x10000u32, 17),
(0x23456u32, 42),
(0x7FFF_FFFFu32, 64),
];
for (rng, nbits_total) in samples {
ctx.rng = rng;
ctx.nbits_total = nbits_total;
let fast = ec_tell_frac(&ctx);
let slow = slow_tell_frac(&ctx);
assert_eq!(fast, slow, "rng={rng:#x}, nbits_total={nbits_total}");
}
}
#[test]
fn small_division_matches_builtin() {
for d in 1..=256u32 {
for n in 0..=2048u32 {
assert_eq!(celt_udiv(n, d), n / d, "n={n}, d={d}");
}
}
let samples = [0u32, 1, 7, 255, 256, 65_535, 1_048_575, u32::MAX];
for &n in &samples {
for d in [3u32, 17, 63, 127, 181, 233, 255, 256] {
assert_eq!(celt_udiv(n, d), n / d, "n={n}, d={d}");
}
}
}
#[test]
fn signed_division_matches_builtin() {
let denominators = [1, 2, 3, 7, 19, 127, 255];
let numerators = [-65_535, -1234, -1, 0, 1, 1234, 65_535, OpusInt32::MAX];
for &d in &denominators {
for &n in &numerators {
assert_eq!(celt_sudiv(n, d), n / d, "n={n}, d={d}");
}
}
}
}