use crate::range_decoder::RangeDecoder;
pub const DYNALLOC_LOGP_INIT: u32 = 6;
pub const DYNALLOC_LOGP_MIN: u32 = 2;
pub const DYNALLOC_LOOP_LOGP_AFTER_FIRST: u32 = 1;
pub const BAND_BOOST_QUANTA_FLOOR_EIGHTH_BITS: u32 = 48;
pub const BAND_BOOST_QUANTA_CEIL_MULT: u32 = 8;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BandBoostError {
CapsLengthMismatch { expected: usize, provided: usize },
NBinsLengthMismatch { expected: usize, provided: usize },
EmptyBandWindow { start: usize, end: usize },
InvertedBandWindow { start: usize, end: usize },
}
pub const fn band_boost_quanta(n_bins_per_channel: u32) -> u32 {
let floored = if n_bins_per_channel > BAND_BOOST_QUANTA_FLOOR_EIGHTH_BITS {
n_bins_per_channel
} else {
BAND_BOOST_QUANTA_FLOOR_EIGHTH_BITS
};
let ceiling = BAND_BOOST_QUANTA_CEIL_MULT * n_bins_per_channel;
if ceiling < floored {
ceiling
} else {
floored
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct BandBoost {
pub boost_eighth_bits: u32,
pub bits_read: u32,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BandBoostOutcome {
pub per_band: Vec<BandBoost>,
pub total_boost_eighth_bits: u32,
pub total_bits_remaining_eighth_bits: u32,
pub dynalloc_logp_final: u32,
}
pub fn decode_band_boosts(
rd: &mut RangeDecoder<'_>,
start: usize,
end: usize,
caps: &[u32],
n_bins: &[u32],
frame_size_bytes: u32,
) -> Result<BandBoostOutcome, BandBoostError> {
if start > end {
return Err(BandBoostError::InvertedBandWindow { start, end });
}
if start == end {
return Err(BandBoostError::EmptyBandWindow { start, end });
}
let band_count = end - start;
if caps.len() != band_count {
return Err(BandBoostError::CapsLengthMismatch {
expected: band_count,
provided: caps.len(),
});
}
if n_bins.len() != band_count {
return Err(BandBoostError::NBinsLengthMismatch {
expected: band_count,
provided: n_bins.len(),
});
}
let mut total_bits_eighth: u32 = frame_size_bytes.saturating_mul(64);
let mut total_boost_eighth: u32 = 0;
let mut dynalloc_logp = DYNALLOC_LOGP_INIT;
let mut per_band: Vec<BandBoost> = Vec::with_capacity(band_count);
for i in 0..band_count {
let cap_band = caps[i];
let quanta = band_boost_quanta(n_bins[i]);
let mut boost: u32 = 0;
let mut dynalloc_loop_logp = dynalloc_logp;
let mut bits_read: u32 = 0;
loop {
let tell_frac = rd.tell_frac();
let logp_eighth = dynalloc_loop_logp.saturating_mul(8);
let budget_eighth = total_bits_eighth.saturating_add(total_boost_eighth);
let projected_tell = tell_frac.saturating_add(logp_eighth);
if projected_tell >= budget_eighth {
break;
}
if boost >= cap_band {
break;
}
let bit = rd.dec_bit_logp(dynalloc_loop_logp);
bits_read = bits_read.saturating_add(1);
if bit == 0 {
break;
}
boost = boost.saturating_add(quanta);
total_boost_eighth = total_boost_eighth.saturating_add(quanta);
total_bits_eighth = total_bits_eighth.saturating_sub(quanta);
dynalloc_loop_logp = DYNALLOC_LOOP_LOGP_AFTER_FIRST;
}
if boost > 0 && dynalloc_logp > DYNALLOC_LOGP_MIN {
dynalloc_logp -= 1;
}
per_band.push(BandBoost {
boost_eighth_bits: boost,
bits_read,
});
}
Ok(BandBoostOutcome {
per_band,
total_boost_eighth_bits: total_boost_eighth,
total_bits_remaining_eighth_bits: total_bits_eighth,
dynalloc_logp_final: dynalloc_logp,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn dynalloc_logp_init_is_six() {
assert_eq!(DYNALLOC_LOGP_INIT, 6);
}
#[test]
fn dynalloc_logp_min_is_two() {
assert_eq!(DYNALLOC_LOGP_MIN, 2);
}
#[test]
fn dynalloc_loop_logp_after_first_is_one() {
assert_eq!(DYNALLOC_LOOP_LOGP_AFTER_FIRST, 1);
}
#[test]
fn quanta_floor_constant_matches_six_bits() {
assert_eq!(BAND_BOOST_QUANTA_FLOOR_EIGHTH_BITS, 48);
assert_eq!(BAND_BOOST_QUANTA_FLOOR_EIGHTH_BITS / 8, 6);
}
#[test]
fn quanta_ceil_mult_is_eight_for_one_bit_per_sample() {
assert_eq!(BAND_BOOST_QUANTA_CEIL_MULT, 8);
}
#[test]
fn quanta_for_n_equals_48_is_48() {
assert_eq!(band_boost_quanta(48), 48);
}
#[test]
fn quanta_above_48_equals_n() {
for n in [49u32, 64, 96, 128, 176] {
assert_eq!(band_boost_quanta(n), n, "N = {}", n);
}
}
#[test]
fn quanta_below_48_floors_at_48_until_ceiling_kicks_in() {
for n in 6u32..48 {
assert_eq!(band_boost_quanta(n), 48, "N = {}", n);
}
}
#[test]
fn quanta_below_six_is_eight_n() {
for n in 1u32..6 {
assert_eq!(band_boost_quanta(n), 8 * n, "N = {}", n);
}
}
#[test]
fn quanta_at_zero_is_zero() {
assert_eq!(band_boost_quanta(0), 0);
}
#[test]
fn quanta_invariants_across_table_55_range() {
for n in [8u32, 16, 24, 32, 48, 64, 88, 128, 176] {
assert!(
band_boost_quanta(n) >= 48,
"N = {} produced {}",
n,
band_boost_quanta(n)
);
}
}
fn rd<'a>(buf: &'a [u8]) -> RangeDecoder<'a> {
RangeDecoder::new(buf)
}
#[test]
fn empty_band_window_rejected() {
let buf = [0x80u8; 8];
let mut d = rd(&buf);
let err = decode_band_boosts(&mut d, 0, 0, &[], &[], 60).unwrap_err();
assert_eq!(err, BandBoostError::EmptyBandWindow { start: 0, end: 0 });
}
#[test]
fn inverted_band_window_rejected() {
let buf = [0x80u8; 8];
let mut d = rd(&buf);
let err = decode_band_boosts(&mut d, 5, 3, &[], &[], 60).unwrap_err();
assert_eq!(err, BandBoostError::InvertedBandWindow { start: 5, end: 3 });
}
#[test]
fn caps_length_mismatch_rejected() {
let buf = [0x80u8; 8];
let mut d = rd(&buf);
let err = decode_band_boosts(&mut d, 0, 5, &[100, 100, 100, 100], &[8, 8, 8, 8, 8], 60)
.unwrap_err();
assert_eq!(
err,
BandBoostError::CapsLengthMismatch {
expected: 5,
provided: 4,
}
);
}
#[test]
fn n_bins_length_mismatch_rejected() {
let buf = [0x80u8; 8];
let mut d = rd(&buf);
let err = decode_band_boosts(&mut d, 0, 3, &[100, 100, 100], &[8, 8], 60).unwrap_err();
assert_eq!(
err,
BandBoostError::NBinsLengthMismatch {
expected: 3,
provided: 2,
}
);
}
#[test]
fn argument_validation_does_not_touch_range_coder() {
let buf = [0x80u8; 8];
let mut d = rd(&buf);
let before = d.tell_frac();
let _ = decode_band_boosts(&mut d, 5, 3, &[], &[], 60);
let _ = decode_band_boosts(&mut d, 0, 0, &[], &[], 60);
let _ = decode_band_boosts(&mut d, 0, 3, &[1, 2], &[1, 2, 3], 60);
let _ = decode_band_boosts(&mut d, 0, 3, &[1, 2, 3], &[1, 2], 60);
assert_eq!(d.tell_frac(), before);
}
#[test]
fn no_room_for_any_boost_returns_all_zeros() {
let buf = [0x80u8; 8];
let mut d = rd(&buf);
let outcome = decode_band_boosts(&mut d, 0, 3, &[100, 100, 100], &[8, 8, 8], 0).unwrap();
for band in &outcome.per_band {
assert_eq!(band.boost_eighth_bits, 0);
assert_eq!(band.bits_read, 0);
}
assert_eq!(outcome.total_boost_eighth_bits, 0);
assert_eq!(outcome.dynalloc_logp_final, DYNALLOC_LOGP_INIT);
}
#[test]
fn no_room_outcome_preserves_total_bits_at_zero() {
let buf = [0x80u8; 8];
let mut d = rd(&buf);
let outcome = decode_band_boosts(&mut d, 0, 3, &[100, 100, 100], &[8, 8, 8], 0).unwrap();
assert_eq!(outcome.total_bits_remaining_eighth_bits, 0);
assert_eq!(outcome.total_boost_eighth_bits, 0);
}
const PAYLOAD_STOP_BIASED: [u8; 64] = [0x00u8; 64];
const PAYLOAD_BOOST_BIASED: [u8; 64] = [0xFFu8; 64];
#[test]
fn stop_biased_payload_decodes_no_boosts() {
let mut d = rd(&PAYLOAD_STOP_BIASED);
let before = d.tell_frac();
let outcome = decode_band_boosts(
&mut d,
0,
5,
&[1000, 1000, 1000, 1000, 1000],
&[48; 5],
1275,
)
.unwrap();
for band in &outcome.per_band {
assert_eq!(band.boost_eighth_bits, 0, "boost not zero: {:?}", band);
assert_eq!(band.bits_read, 1, "expected 1 bit: {:?}", band);
}
assert_eq!(outcome.total_boost_eighth_bits, 0);
assert_eq!(outcome.dynalloc_logp_final, DYNALLOC_LOGP_INIT);
assert!(d.tell_frac() > before);
}
#[test]
fn boost_biased_payload_actually_boosts_at_least_one_band() {
let mut d = rd(&PAYLOAD_BOOST_BIASED);
let outcome = decode_band_boosts(
&mut d,
0,
5,
&[1000, 1000, 1000, 1000, 1000],
&[48; 5],
1275,
)
.unwrap();
let any_boosted = outcome.per_band.iter().any(|b| b.boost_eighth_bits > 0);
assert!(
any_boosted,
"boost-biased payload did not boost: {:?}",
outcome
);
let sum: u32 = outcome.per_band.iter().map(|b| b.boost_eighth_bits).sum();
assert_eq!(sum, outcome.total_boost_eighth_bits);
}
#[test]
fn boost_biased_payload_decrements_dynalloc_logp() {
let mut d = rd(&PAYLOAD_BOOST_BIASED);
let outcome = decode_band_boosts(&mut d, 0, 10, &[1000; 10], &[48; 10], 1275).unwrap();
assert!(outcome.dynalloc_logp_final < DYNALLOC_LOGP_INIT);
assert!(outcome.dynalloc_logp_final >= DYNALLOC_LOGP_MIN);
}
#[test]
fn band_count_matches_per_band_length() {
let mut d = rd(&PAYLOAD_STOP_BIASED);
let outcome = decode_band_boosts(&mut d, 0, 7, &[1000; 7], &[48; 7], 1275).unwrap();
assert_eq!(outcome.per_band.len(), 7);
}
#[test]
fn hybrid_start_window_yields_four_bands_with_end_21() {
let mut d = rd(&PAYLOAD_STOP_BIASED);
let outcome = decode_band_boosts(&mut d, 17, 21, &[1000; 4], &[48; 4], 1275).unwrap();
assert_eq!(outcome.per_band.len(), 4);
}
#[test]
fn total_bits_plus_total_boost_equals_frame_budget_stop_path() {
let mut d = rd(&PAYLOAD_STOP_BIASED);
let outcome = decode_band_boosts(&mut d, 0, 5, &[1000; 5], &[48; 5], 1275).unwrap();
let sum = outcome
.total_bits_remaining_eighth_bits
.saturating_add(outcome.total_boost_eighth_bits);
assert_eq!(sum, 1275 * 64);
}
#[test]
fn total_bits_plus_total_boost_equals_frame_budget_boost_path() {
let mut d = rd(&PAYLOAD_BOOST_BIASED);
let outcome = decode_band_boosts(&mut d, 0, 10, &[1000; 10], &[48; 10], 1275).unwrap();
let sum = outcome
.total_bits_remaining_eighth_bits
.saturating_add(outcome.total_boost_eighth_bits);
assert_eq!(sum, 1275 * 64);
}
#[test]
fn dynalloc_logp_floors_at_min_after_many_boosts() {
let mut dynalloc = DYNALLOC_LOGP_INIT;
for _ in 0..21 {
if dynalloc > DYNALLOC_LOGP_MIN {
dynalloc -= 1;
}
}
assert_eq!(dynalloc, DYNALLOC_LOGP_MIN);
}
#[test]
fn dynalloc_logp_only_decrements_when_band_actually_boosted() {
let mut d = rd(&PAYLOAD_STOP_BIASED);
let outcome = decode_band_boosts(&mut d, 0, 21, &[1000; 21], &[48; 21], 1275).unwrap();
assert_eq!(outcome.dynalloc_logp_final, DYNALLOC_LOGP_INIT);
}
#[test]
fn band_boost_default_is_zero() {
let bb = BandBoost::default();
assert_eq!(bb.boost_eighth_bits, 0);
assert_eq!(bb.bits_read, 0);
}
#[test]
fn per_band_outcome_alignment_with_window() {
let mut d = rd(&PAYLOAD_STOP_BIASED);
let outcome = decode_band_boosts(&mut d, 3, 10, &[500; 7], &[64; 7], 1275).unwrap();
assert_eq!(outcome.per_band.len(), 10 - 3);
}
#[test]
fn boost_does_not_grossly_exceed_cap() {
for cap in [0u32, 48, 96, 144, 1000] {
for quanta in [48u32, 64, 96] {
let max_iters = match cap.checked_div(quanta) {
Some(q) => q + 1,
None => 0,
};
let max_boost = max_iters * quanta;
assert!(
max_boost <= cap + quanta,
"cap={} quanta={} max_boost={}",
cap,
quanta,
max_boost
);
}
}
}
#[test]
fn zero_cap_band_yields_zero_boost() {
let mut d = rd(&PAYLOAD_BOOST_BIASED);
let before = d.tell_frac();
let outcome = decode_band_boosts(&mut d, 0, 3, &[0, 0, 0], &[48, 48, 48], 1275).unwrap();
for band in &outcome.per_band {
assert_eq!(band.boost_eighth_bits, 0);
assert_eq!(band.bits_read, 0);
}
assert_eq!(d.tell_frac(), before);
assert_eq!(outcome.total_boost_eighth_bits, 0);
}
#[test]
fn band_boost_quanta_is_total_function_over_u16() {
for n in 0u32..=u16::MAX as u32 {
let _ = band_boost_quanta(n);
}
}
#[test]
fn dynalloc_logp_final_within_min_max() {
let mut d = rd(&PAYLOAD_BOOST_BIASED);
let outcome = decode_band_boosts(&mut d, 0, 21, &[1000; 21], &[48; 21], 1275).unwrap();
assert!(outcome.dynalloc_logp_final >= DYNALLOC_LOGP_MIN);
assert!(outcome.dynalloc_logp_final <= DYNALLOC_LOGP_INIT);
}
#[test]
fn rfc_eighth_bit_unit_conversion_pinned() {
assert_eq!(DYNALLOC_LOGP_INIT * 8, BAND_BOOST_QUANTA_FLOOR_EIGHTH_BITS);
}
#[test]
fn budget_sum_invariant_at_zero_frame_bytes() {
let buf = [0xFFu8; 8];
let mut d = RangeDecoder::new(&buf);
let outcome = decode_band_boosts(&mut d, 0, 1, &[1000], &[48], 0).unwrap();
assert_eq!(outcome.total_bits_remaining_eighth_bits, 0);
assert_eq!(outcome.total_boost_eighth_bits, 0);
}
#[test]
fn frame_size_max_opus_does_not_overflow() {
assert!(1275u32.checked_mul(64).is_some());
let mut d = rd(&PAYLOAD_STOP_BIASED);
let outcome = decode_band_boosts(&mut d, 0, 21, &[1000; 21], &[48; 21], 1275).unwrap();
let sum = outcome
.total_bits_remaining_eighth_bits
.saturating_add(outcome.total_boost_eighth_bits);
assert_eq!(sum, 1275 * 64);
}
#[test]
fn outcome_equality_holds_for_identical_runs() {
let mut d1 = rd(&PAYLOAD_STOP_BIASED);
let mut d2 = rd(&PAYLOAD_STOP_BIASED);
let a = decode_band_boosts(&mut d1, 0, 5, &[100; 5], &[48; 5], 1275).unwrap();
let b = decode_band_boosts(&mut d2, 0, 5, &[100; 5], &[48; 5], 1275).unwrap();
assert_eq!(a, b);
}
}