use super::{
decode_num_coding_passes, push_segment_or_record_error, read_lblock_increment,
read_segment_length, resolve_code_block_inclusion, PacketResult, MAX_BITPLANE_COUNT,
};
use crate::error::DecodeError;
use crate::j2c::build::{CodeBlock, CodeBlockCoding, Segment};
use crate::j2c::codestream::{CodeBlockStyle, ComponentInfo};
use crate::j2c::decode::DecompositionStorage;
use crate::j2c::progression::ProgressionData;
use crate::reader::BitReader;
pub(super) fn resolve_segments(
sub_band_idx: usize,
progression_data: &ProgressionData,
reader: &mut BitReader<'_>,
storage: &mut DecompositionStorage<'_>,
component_info: &ComponentInfo,
) -> PacketResult {
let sub_band = &storage.sub_bands[sub_band_idx];
let precincts = &mut storage.precincts[sub_band.precincts.clone()];
let precinct_index = usize::try_from(progression_data.precinct)
.map_err(|_| "classic packet precinct index does not fit the platform")?;
let Some(precinct) = precincts.get_mut(precinct_index) else {
lwarn!("progression data yielded invalid precinct index");
return Err("classic packet references a missing precinct");
};
let code_blocks = &mut storage.code_blocks[precinct.code_blocks.clone()];
for code_block in code_blocks {
let inclusion = resolve_code_block_inclusion(
code_block,
precinct,
progression_data,
reader,
&mut storage.tag_tree_nodes,
)
.ok_or("classic code-block inclusion or zero-bitplane tree is truncated")?;
if !inclusion.included {
continue;
}
let layer = storage.layers[code_block.layers.clone()]
.get_mut(usize::from(progression_data.layer_num))
.ok_or("classic packet references a missing quality layer")?;
let added_coding_passes = decode_num_coding_passes(reader)
.ok_or("classic coding-pass count is truncated or invalid")?;
code_block.l_block = code_block
.l_block
.checked_add(
read_lblock_increment(reader).ok_or("classic Lblock increment is truncated")?,
)
.ok_or("classic Lblock increment overflows")?;
let start = storage.segments.len();
parse_contribution_lengths(
reader,
added_coding_passes,
None,
code_block,
component_info.code_block_style(),
&mut storage.segments,
storage.structural_workspace_bytes,
&mut storage.packet_workspace_error,
)?;
layer.segments = Some(start..storage.segments.len());
code_block.coding = Some(CodeBlockCoding::Classic);
code_block.non_empty_layer_count = code_block
.non_empty_layer_count
.checked_add(1)
.ok_or("classic non-empty layer count overflows")?;
}
Ok(())
}
#[expect(
clippy::too_many_arguments,
reason = "packet parsing keeps the code-block state, bounded segment owner, and input cursor explicit"
)]
pub(super) fn parse_contribution_lengths(
reader: &mut BitReader<'_>,
added_coding_passes: u8,
mut first_length: Option<u32>,
code_block: &mut CodeBlock,
style: CodeBlockStyle,
segments: &mut alloc::vec::Vec<Segment<'_>>,
structural_workspace_bytes: usize,
packet_workspace_error: &mut Option<DecodeError>,
) -> PacketResult {
const MAX_CODING_PASSES: u8 = 1 + 3 * (MAX_BITPLANE_COUNT - 1);
let previous_passes = code_block.number_of_coding_passes;
let cumulative_passes = previous_passes
.checked_add(added_coding_passes)
.ok_or("classic cumulative coding-pass count overflows")?;
if cumulative_passes > MAX_CODING_PASSES {
return Err("classic cumulative coding-pass count exceeds the supported maximum");
}
let segment_idx = |pass_idx: u8| {
if style.termination_on_each_pass {
pass_idx
} else if style.selective_arithmetic_coding_bypass {
segment_idx_for_bypass(pass_idx)
} else {
code_block.non_empty_layer_count
}
};
let mut push_segment = |idx: u8, coding_passes: u8| -> PacketResult {
let length = if let Some(length) = first_length.take() {
length
} else {
read_segment_length(reader, code_block.l_block, coding_passes)?
};
push_segment_or_record_error(
segments,
structural_workspace_bytes,
packet_workspace_error,
Segment {
idx,
data_length: length,
coding_pases: coding_passes,
data: &[],
},
)
.ok_or("classic segment metadata allocation failed")
};
let mut last_segment = segment_idx(previous_passes);
let mut passes_in_segment = 0;
for coding_pass in previous_passes..cumulative_passes {
let segment = segment_idx(coding_pass);
if segment == last_segment {
passes_in_segment += 1;
} else {
push_segment(last_segment, passes_in_segment)?;
last_segment = segment;
passes_in_segment = 1;
}
}
if passes_in_segment != 0 {
push_segment(last_segment, passes_in_segment)?;
}
if first_length.is_some() {
return Err("classic contribution did not consume its prefetched length");
}
code_block.number_of_coding_passes = cumulative_passes;
Ok(())
}
fn segment_idx_for_bypass(pass_idx: u8) -> u8 {
if pass_idx < 10 {
0
} else {
1 + (2 * ((pass_idx - 10) / 3)) + u8::from(((pass_idx - 10) % 3) == 2)
}
}