pub const MAX_CW_LEN: [u8; 32] = [
0, 11, 9, 20, 16, 13, 11, 14, 12, 17, 14, 49, 0, 0, 0, 0, 14, 17, 21, 21, 25, 25, 29, 29, 29, 29, 33, 33, 33, 37, 37, 41, ];
pub const CODEBOOK_PRIORITY: [u8; 32] = [
0, 21, 21, 20, 20, 19, 19, 18, 18, 17, 17, 0, 0, 0, 0, 0, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, ];
#[must_use]
pub fn codebook_priority(cb: u8) -> u8 {
CODEBOOK_PRIORITY.get(cb as usize).copied().unwrap_or(0)
}
#[must_use]
pub fn max_cw_len(cb: u8) -> u8 {
MAX_CW_LEN.get(cb as usize).copied().unwrap_or(0)
}
#[must_use]
pub fn assigned_unit_nr(
cb: u8,
max_lines_in_window: u32,
nr_of_first_line_in_unit: u32,
max_windows: u32,
window: u32,
) -> u32 {
(u32::from(codebook_priority(cb)) * max_lines_in_window + nr_of_first_line_in_unit)
* max_windows
+ window
}
#[must_use]
pub fn segment_width(cb: u8, length_of_longest_codeword: u8) -> u8 {
max_cw_len(cb).min(clamp_longest_codeword(length_of_longest_codeword))
}
#[must_use]
pub fn clamp_longest_codeword(length_of_longest_codeword: u8) -> u8 {
length_of_longest_codeword.min(49)
}
#[must_use]
pub fn clamp_reordered_length(length_of_reordered_spectral_data: u16, is_cpe: bool) -> u16 {
let max = if is_cpe { 12288 } else { 6144 };
length_of_reordered_spectral_data.min(max)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Segmentation {
pub segment_bits: Vec<u32>,
pub total_bits: u32,
}
impl Segmentation {
#[must_use]
pub fn new(pcw_segment_widths: &[u8], total_bits: u32) -> Self {
let mut segment_bits: Vec<u32> = Vec::new();
if total_bits == 0 {
return Segmentation {
segment_bits,
total_bits,
};
}
let mut used: u32 = 0;
for &w in pcw_segment_widths {
let w = u32::from(w);
if used + w > total_bits {
break;
}
segment_bits.push(w);
used += w;
}
let remainder = total_bits - used;
if remainder > 0 {
if let Some(last) = segment_bits.last_mut() {
*last += remainder;
} else {
segment_bits.push(remainder);
}
}
Segmentation {
segment_bits,
total_bits,
}
}
#[must_use]
pub fn number_of_segments(&self) -> usize {
self.segment_bits.len()
}
#[must_use]
pub(crate) fn segment_start(&self, i: usize) -> u32 {
self.segment_bits[..i].iter().sum()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
Forward,
Backward,
}
impl Direction {
#[must_use]
pub fn toggled(self) -> Self {
match self {
Direction::Forward => Direction::Backward,
Direction::Backward => Direction::Forward,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ReorderPlan {
pub codeword_bits: Vec<Vec<u32>>,
}
impl ReorderPlan {
#[must_use]
pub fn build(codeword_lengths: &[u32], seg: &Segmentation) -> Option<Self> {
let num_segments = seg.number_of_segments();
let num_codewords = codeword_lengths.len();
if num_segments == 0 {
return if num_codewords == 0 {
Some(ReorderPlan {
codeword_bits: Vec::new(),
})
} else {
None
};
}
let widths: Vec<u32> = seg.segment_bits.clone();
let mut low: Vec<u32> = vec![0; num_segments];
let mut high: Vec<u32> = vec![0; num_segments];
let seg_start: Vec<u32> = (0..num_segments).map(|s| seg.segment_start(s)).collect();
let mut codeword_bits: Vec<Vec<u32>> = vec![Vec::new(); num_codewords];
let mut remaining: Vec<u32> = codeword_lengths.to_vec();
macro_rules! write_cw_to_seg {
($cw:expr, $sg:expr, $dir:expr) => {{
let cw = $cw;
let sg = $sg;
let dir = $dir;
let free = widths[sg] - low[sg] - high[sg];
let n = remaining[cw].min(free);
for _ in 0..n {
let local = match dir {
Direction::Forward => {
let l = low[sg];
low[sg] += 1;
l
}
Direction::Backward => {
let l = widths[sg] - 1 - high[sg];
high[sg] += 1;
l
}
};
codeword_bits[cw].push(seg_start[sg] + local);
}
remaining[cw] -= n;
n
}};
}
for codeword in 0..num_segments.min(num_codewords) {
write_cw_to_seg!(codeword, codeword, Direction::Forward);
}
let num_sets = num_codewords.div_ceil(num_segments);
let mut write_direction = Direction::Forward;
for set in 1..num_sets {
write_direction = write_direction.toggled();
for trial in 0..num_segments {
for codeword_base in 0..num_segments {
let segment = (trial + codeword_base) % num_segments;
let codeword = codeword_base + set * num_segments;
if codeword >= num_codewords {
continue;
}
if remaining[codeword] > 0 {
write_cw_to_seg!(codeword, segment, write_direction);
}
}
}
}
if remaining.iter().any(|&r| r > 0) {
return None;
}
Some(ReorderPlan { codeword_bits })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn max_cw_len_table_spot_checks() {
assert_eq!(max_cw_len(0), 0);
assert_eq!(max_cw_len(1), 11);
assert_eq!(max_cw_len(3), 20);
assert_eq!(max_cw_len(11), 49);
assert_eq!(max_cw_len(13), 0);
assert_eq!(max_cw_len(15), 0);
assert_eq!(max_cw_len(16), 14);
assert_eq!(max_cw_len(31), 41);
assert_eq!(max_cw_len(200), 0);
}
#[test]
fn codebook_priority_table_spot_checks() {
assert_eq!(codebook_priority(0), 0);
assert_eq!(codebook_priority(1), 21);
assert_eq!(codebook_priority(2), 21);
assert_eq!(codebook_priority(10), 17);
assert_eq!(codebook_priority(11), 0);
assert_eq!(codebook_priority(16), 16);
assert_eq!(codebook_priority(31), 1);
assert_eq!(codebook_priority(99), 0);
}
#[test]
fn assigned_unit_nr_long_window() {
assert_eq!(assigned_unit_nr(1, 1024, 0, 1, 0), 21504);
assert_eq!(assigned_unit_nr(11, 1024, 0, 1, 0), 0);
assert!(assigned_unit_nr(1, 1024, 4, 1, 0) > assigned_unit_nr(31, 1024, 4, 1, 0));
}
#[test]
fn assigned_unit_nr_short_window_interleaves_window() {
let a = assigned_unit_nr(5, 128, 8, 8, 0);
let b = assigned_unit_nr(5, 128, 8, 8, 3);
assert_eq!(b - a, 3);
}
#[test]
fn segment_width_is_min_of_maxcwlen_and_longest() {
assert_eq!(segment_width(3, 16), 16);
assert_eq!(segment_width(2, 16), 9);
assert_eq!(segment_width(11, 60), 49);
}
#[test]
fn clamp_reordered_length_per_element_kind() {
assert_eq!(clamp_reordered_length(7000, false), 6144);
assert_eq!(clamp_reordered_length(7000, true), 7000);
assert_eq!(clamp_reordered_length(20000, true), 12288);
assert_eq!(clamp_reordered_length(100, false), 100);
}
#[test]
fn segmentation_folds_remainder_into_last_segment() {
let seg = Segmentation::new(&[10, 10, 10], 35);
assert_eq!(seg.segment_bits, vec![10, 10, 15]);
assert_eq!(seg.number_of_segments(), 3);
assert_eq!(seg.segment_bits.iter().sum::<u32>(), 35);
}
#[test]
fn segmentation_stops_before_overrun() {
let seg = Segmentation::new(&[10, 10, 10], 25);
assert_eq!(seg.segment_bits, vec![10, 15]);
assert_eq!(seg.segment_bits.iter().sum::<u32>(), 25);
}
#[test]
fn segmentation_single_segment_when_first_width_exceeds_buffer() {
let seg = Segmentation::new(&[50, 50], 30);
assert_eq!(seg.segment_bits, vec![30]);
assert_eq!(seg.number_of_segments(), 1);
}
#[test]
fn segmentation_zero_buffer_is_empty() {
let seg = Segmentation::new(&[10, 10], 0);
assert!(seg.segment_bits.is_empty());
assert_eq!(seg.number_of_segments(), 0);
assert_eq!(seg.total_bits, 0);
}
#[test]
fn segmentation_exact_fit_no_remainder() {
let seg = Segmentation::new(&[8, 8, 8], 24);
assert_eq!(seg.segment_bits, vec![8, 8, 8]);
assert_eq!(seg.segment_bits.iter().sum::<u32>(), 24);
}
fn assert_bijective(plan: &ReorderPlan, total_bits: u32) {
let mut seen = vec![false; total_bits as usize];
let mut count = 0u32;
for cw in &plan.codeword_bits {
for &p in cw {
assert!(p < total_bits, "position {p} out of range");
assert!(!seen[p as usize], "position {p} written twice");
seen[p as usize] = true;
count += 1;
}
}
assert_eq!(count, total_bits, "not every buffer bit was covered");
}
#[test]
fn reorder_pcws_start_at_segment_boundaries() {
let seg = Segmentation::new(&[8, 8, 8], 24);
let plan = ReorderPlan::build(&[8, 8, 8], &seg).unwrap();
assert_eq!(plan.codeword_bits[0], (0..8).collect::<Vec<_>>());
assert_eq!(plan.codeword_bits[1], (8..16).collect::<Vec<_>>());
assert_eq!(plan.codeword_bits[2], (16..24).collect::<Vec<_>>());
assert_bijective(&plan, 24);
}
#[test]
fn reorder_nonpcws_fill_gaps_with_direction_toggle() {
let seg = Segmentation::new(&[10, 10], 20);
let plan = ReorderPlan::build(&[4, 4, 6, 6], &seg).unwrap();
assert_eq!(plan.codeword_bits[0], vec![0, 1, 2, 3]);
assert_eq!(plan.codeword_bits[1], vec![10, 11, 12, 13]);
assert_eq!(plan.codeword_bits[2], vec![9, 8, 7, 6, 5, 4]);
assert_eq!(plan.codeword_bits[3], vec![19, 18, 17, 16, 15, 14]);
assert_bijective(&plan, 20);
}
#[test]
fn reorder_codeword_spanning_multiple_segments() {
let seg = Segmentation::new(&[5, 5, 5], 15);
let plan = ReorderPlan::build(&[3, 3, 3, 2, 2, 2], &seg).unwrap();
assert_bijective(&plan, 15);
assert_eq!(plan.codeword_bits[0], vec![0, 1, 2]);
assert_eq!(plan.codeword_bits[1], vec![5, 6, 7]);
assert_eq!(plan.codeword_bits[2], vec![10, 11, 12]);
}
#[test]
fn reorder_partial_codeword_continues_next_trial() {
let seg = Segmentation::new(&[6, 6], 12);
let plan = ReorderPlan::build(&[2, 2, 6, 2], &seg).unwrap();
assert_bijective(&plan, 12);
assert_eq!(plan.codeword_bits[2].len(), 6);
assert_eq!(plan.codeword_bits[3].len(), 2);
}
#[test]
fn reorder_rejects_overfull_buffer() {
let seg = Segmentation::new(&[8, 8], 16);
assert!(ReorderPlan::build(&[8, 8, 4], &seg).is_none());
}
#[test]
fn reorder_empty_block() {
let seg = Segmentation::new(&[], 0);
let plan = ReorderPlan::build(&[], &seg).unwrap();
assert!(plan.codeword_bits.is_empty());
}
}