use super::EncodedImageError;
use crate::ByteAlignment;
use crate::image::{
CoverageBudget, ReferenceMode, SurfaceDescriptor, UNIT_GROUP_RECORD_LEN, UnitGroup,
UnitGroupRecord,
};
use crate::media::{CodingRecord, CodingTable, CodingTableError};
use core::{iter::FusedIterator, ops::Range};
#[cfg(test)]
mod tests;
#[derive(Clone, Copy)]
pub(crate) enum CodingRecords<'a> {
Wire(CodingTable<'a>),
Native(&'a [CodingRecord<'a>]),
Groups(&'a [UnitGroup<'a>]),
}
impl<'a> CodingRecords<'a> {
pub(crate) fn len(self) -> usize {
match self {
Self::Wire(table) => table.len(),
Self::Native(records) => records.len(),
Self::Groups(groups) => groups
.iter()
.enumerate()
.filter(|(index, group)| {
!groups[..*index]
.iter()
.any(|candidate| candidate.coding() == group.coding())
})
.count(),
}
}
pub(crate) fn get(self, index: usize) -> Option<CodingRecord<'a>> {
self.iter().nth(index)
}
pub(crate) fn iter(self) -> CodingRecordsIter<'a> {
CodingRecordsIter {
source: self,
front: 0,
back: match self {
Self::Wire(table) => table.len(),
Self::Native(records) => records.len(),
Self::Groups(groups) => groups.len(),
},
remaining: self.len(),
}
}
pub(crate) fn encoded_len(self) -> Result<usize, CodingTableError> {
match self {
Self::Wire(table) => Ok(table.byte_len()),
Self::Native(records) => CodingTable::encoded_len(records),
Self::Groups(_) => CodingTable::encoded_iter_len(self.iter()),
}
}
pub(crate) fn index_of(self, coding: CodingRecord<'_>) -> Option<u32> {
self.iter()
.position(|candidate| candidate == coding)
.and_then(|index| u32::try_from(index).ok())
}
}
#[derive(Clone)]
pub(crate) struct CodingRecordsIter<'a> {
source: CodingRecords<'a>,
front: usize,
back: usize,
remaining: usize,
}
impl<'a> CodingRecordsIter<'a> {
fn get(&self, index: usize) -> Option<CodingRecord<'a>> {
match self.source {
CodingRecords::Wire(table) => table.get(index),
CodingRecords::Native(records) => records.get(index).copied(),
CodingRecords::Groups(groups) => groups.get(index).map(|group| group.coding()),
}
}
fn is_unique(&self, index: usize, coding: CodingRecord<'_>) -> bool {
match self.source {
CodingRecords::Groups(groups) => !groups[..index]
.iter()
.any(|candidate| candidate.coding() == coding),
_ => true,
}
}
}
impl<'a> Iterator for CodingRecordsIter<'a> {
type Item = CodingRecord<'a>;
fn next(&mut self) -> Option<Self::Item> {
while self.front < self.back {
let index = self.front;
self.front += 1;
let coding = self.get(index)?;
if self.is_unique(index, coding) {
self.remaining -= 1;
return Some(coding);
}
}
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.remaining, Some(self.remaining))
}
}
impl DoubleEndedIterator for CodingRecordsIter<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
while self.front < self.back {
self.back -= 1;
let coding = self.get(self.back)?;
if self.is_unique(self.back, coding) {
self.remaining -= 1;
return Some(coding);
}
}
None
}
}
impl ExactSizeIterator for CodingRecordsIter<'_> {}
impl FusedIterator for CodingRecordsIter<'_> {}
#[derive(Clone, Copy)]
pub(crate) enum GroupRecords<'a> {
Implicit,
Wire(&'a [u8]),
Native(&'a [UnitGroupRecord]),
}
#[derive(Clone, Copy)]
pub(crate) struct GroupSource<'a> {
pub(crate) surface: SurfaceDescriptor,
pub(crate) codings: CodingRecords<'a>,
pub(crate) records: GroupRecords<'a>,
pub(crate) data: &'a [u8],
pub(crate) indexes: &'a [u8],
pub(crate) file_offset: Option<u32>,
pub(crate) data_offset: u32,
}
impl<'a> GroupSource<'a> {
pub(crate) fn group_count(self) -> usize {
match self.records {
GroupRecords::Implicit => 1,
GroupRecords::Wire(bytes) => bytes.len() / UNIT_GROUP_RECORD_LEN,
GroupRecords::Native(records) => records.len(),
}
}
pub(crate) fn validate_tables(self) -> Result<(), EncodedImageError> {
u32::try_from(self.data.len()).map_err(|_| EncodedImageError::SizeOverflow)?;
u32::try_from(self.indexes.len()).map_err(|_| EncodedImageError::SizeOverflow)?;
if self.codings.len() == 0 {
return Err(EncodedImageError::Codings(CodingTableError::EmptyTable));
}
match self.records {
GroupRecords::Implicit if self.codings.len() != 1 || !self.indexes.is_empty() => {
Err(EncodedImageError::AmbiguousImplicitGroup)
}
GroupRecords::Wire(bytes)
if bytes.is_empty() || bytes.len() % UNIT_GROUP_RECORD_LEN != 0 =>
{
Err(EncodedImageError::InvalidGroupTableLength(bytes.len()))
}
GroupRecords::Native([]) => Err(EncodedImageError::InvalidGroupTableLength(0)),
_ => Ok(()),
}
}
pub(crate) fn record(self, index: usize) -> Result<UnitGroupRecord, EncodedImageError> {
if index >= self.group_count() {
return Err(EncodedImageError::GroupOutOfBounds(index));
}
let record = match self.records {
GroupRecords::Implicit => UnitGroupRecord::new(0, 0..self.data.len() as u32),
GroupRecords::Wire(bytes) => {
UnitGroupRecord::open(&bytes[index * UNIT_GROUP_RECORD_LEN..])
}
GroupRecords::Native(records) => {
let record = records[index];
record.validate().map(|()| record)
}
};
record.map_err(|error| EncodedImageError::Group { index, error })
}
pub(crate) fn input_alignment(self) -> Result<ByteAlignment, EncodedImageError> {
self.validate_tables()?;
let mut alignment = ByteAlignment::ONE;
for index in 0..self.group_count() {
alignment = alignment.max(self.record(index)?.input_alignment());
}
Ok(alignment)
}
pub(crate) fn validate_groups(
self,
budget: &mut CoverageBudget,
) -> Result<(), EncodedImageError> {
self.visit_groups(Some(budget), |_, _| {})?;
self.surface
.validate_coverage_by(
self.group_count(),
|index, budget| {
let record = self.record(index).expect("validated group record");
budget.spend_many(self.resolution_cost(record))?;
Ok(self
.resolve_record(index, record)
.expect("validated immutable group")
.0)
},
budget,
)
.map_err(EncodedImageError::Coverage)
}
pub(crate) fn validate_group_range(
self,
range: Range<usize>,
budget: &mut CoverageBudget,
) -> Result<(), EncodedImageError> {
debug_assert!(range.end <= self.group_count());
self.surface
.validate_coverage_by(
range.len(),
|relative, budget| {
let index = range.start + relative;
let record = self.record(index).expect("validated group record");
budget.spend_many(self.resolution_cost(record))?;
Ok(self
.resolve_record(index, record)
.expect("validated immutable group")
.0)
},
budget,
)
.map_err(EncodedImageError::Coverage)
}
pub(crate) fn validate_disjoint_group_range(
self,
range: Range<usize>,
budget: &mut CoverageBudget,
) -> Result<(), EncodedImageError> {
debug_assert!(range.end <= self.group_count());
self.surface
.validate_disjoint_coverage_by(
range.len(),
|relative, budget| {
let index = range.start + relative;
let record = self.record(index).expect("validated group record");
budget.spend_many(self.resolution_cost(record))?;
Ok(self
.resolve_record(index, record)
.expect("validated immutable group")
.0)
},
budget,
)
.map_err(EncodedImageError::Coverage)
}
pub(crate) fn resolution_cost(self, record: UnitGroupRecord) -> u64 {
record.resolution_work(self.indexes.len())
}
pub(crate) fn resolve_record(
self,
index: usize,
record: UnitGroupRecord,
) -> Result<(UnitGroup<'a>, core::ops::Range<u32>), EncodedImageError> {
record
.resolve_with(self.surface, self.data, self.indexes, |ordinal| {
self.codings.get(ordinal as usize)
})
.map_err(|error| EncodedImageError::Group { index, error })
}
pub(crate) fn visit_groups(
self,
budget: Option<&mut CoverageBudget>,
visit: impl FnMut(usize, UnitGroup<'a>),
) -> Result<(), EncodedImageError> {
self.visit_groups_inner(false, budget, visit)
}
pub(crate) fn visit_groups_with_references(
self,
budget: Option<&mut CoverageBudget>,
visit: impl FnMut(usize, UnitGroup<'a>),
) -> Result<(), EncodedImageError> {
self.visit_groups_inner(true, budget, visit)
}
fn visit_groups_inner(
self,
allow_references: bool,
mut budget: Option<&mut CoverageBudget>,
mut visit: impl FnMut(usize, UnitGroup<'a>),
) -> Result<(), EncodedImageError> {
self.validate_tables()?;
let index_bytes = self.indexes;
let mut data_end = 0u32;
let mut index_end = 0u32;
for index in 0..self.group_count() {
let record = self.record(index)?;
if let Some(budget) = budget.as_deref_mut() {
budget
.spend_many(self.resolution_cost(record))
.map_err(EncodedImageError::Coverage)?;
}
if !allow_references && record.reference() != ReferenceMode::Independent {
return Err(EncodedImageError::ReferenceInStaticImage(index));
}
let alignment = record.input_alignment();
let alignment_bytes = alignment.get();
let expected_start = data_end
.checked_add(alignment_bytes - 1)
.map(|end| end & !(alignment_bytes - 1))
.ok_or(EncodedImageError::SizeOverflow)?;
if record.data_range().start != expected_start {
return Err(EncodedImageError::NonCanonicalDataRange {
index,
expected_start,
actual_start: record.data_range().start,
});
}
if let Some(file_offset) = self.file_offset {
let absolute_offset = file_offset
.checked_add(self.data_offset)
.and_then(|offset| offset.checked_add(record.data_range().start))
.ok_or(EncodedImageError::SizeOverflow)?;
if absolute_offset % alignment_bytes != 0 {
return Err(EncodedImageError::FileAddressUnaligned {
index,
absolute_offset,
alignment,
});
}
}
let (group, range) = self.resolve_record(index, record)?;
if group.is_empty() && !matches!(self.records, GroupRecords::Implicit) {
return Err(EncodedImageError::EmptyGroup(index));
}
if !range.is_empty() {
if range.start != index_end {
return Err(EncodedImageError::NonCanonicalIndexRange {
index,
expected_start: index_end,
actual_start: range.start,
});
}
index_end = range.end;
}
data_end = record.data_range().end;
visit(index, group);
}
if data_end != self.data.len() as u32 {
return Err(EncodedImageError::UnreferencedData);
}
if index_end as usize != index_bytes.len() {
return Err(EncodedImageError::UnreferencedIndexes);
}
Ok(())
}
}