use super::{
code_block_is_read, decode_num_coding_passes, read_lblock_increment, read_segment_length,
resolve_code_block_inclusion, PacketResult, PacketSkips, SegmentSink, MAX_BITPLANE_COUNT,
};
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;
#[expect(
clippy::too_many_arguments,
reason = "the packet, its precinct's offset among the records, and the parser state it fills stay explicit"
)]
pub(super) fn resolve_segments(
sub_band_idx: usize,
progression_data: &ProgressionData,
precinct_offset: usize,
reader: &mut BitReader<'_>,
storage: &mut DecompositionStorage<'_>,
component_info: &ComponentInfo,
num_layers: u8,
skips: &mut PacketSkips,
) -> PacketResult {
let sub_band = &storage.sub_bands[sub_band_idx];
let precinct_range = sub_band.precincts.clone();
let Some(code_block_range) = storage.precincts[precinct_range.clone()]
.get(precinct_offset)
.map(|precinct| precinct.code_blocks.clone())
else {
lwarn!("progression data yielded invalid precinct index");
return Err("classic packet references a missing precinct");
};
for code_block_idx in code_block_range {
let precinct = &mut storage.precincts[precinct_range.clone()][precinct_offset];
let code_block = &mut storage.code_blocks[code_block_idx];
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 read = code_block_is_read(storage, sub_band_idx, &storage.code_blocks[code_block_idx]);
let layer_num = usize::from(progression_data.layer_num);
let code_block = &mut storage.code_blocks[code_block_idx];
let layer = if read {
Some(
storage.layers[code_block.layers.clone()]
.get_mut(layer_num)
.ok_or("classic packet references a missing quality layer")?,
)
} else if layer_num < usize::from(num_layers) {
None
} else {
return Err("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")?;
if let Some(layer) = layer {
let start = storage.segments.len();
parse_contribution_lengths(
reader,
added_coding_passes,
None,
code_block,
component_info.code_block_style(),
&mut SegmentSink::Record {
segments: &mut storage.segments,
structural_workspace_bytes: storage.structural_workspace_bytes,
packet_workspace_error: &mut storage.packet_workspace_error,
},
)?;
layer.segments = Some(start..storage.segments.len());
} else {
let mut bytes = 0;
parse_contribution_lengths(
reader,
added_coding_passes,
None,
code_block,
component_info.code_block_style(),
&mut SegmentSink::Skip(&mut bytes),
)?;
skips.push(code_block_idx, bytes)?;
}
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(())
}
pub(super) fn parse_contribution_lengths(
reader: &mut BitReader<'_>,
added_coding_passes: u8,
mut first_length: Option<u32>,
code_block: &mut CodeBlock,
style: CodeBlockStyle,
sink: &mut SegmentSink<'_, '_>,
) -> 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)?
};
sink.push(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)
}
}