use core::ops::Range;
use super::{EncodedImageError, EncodedImageLayout};
use crate::{
image::{SURFACE_RECORD_LEN, SurfaceDescriptor},
media::{
MEDIA_SECTION_LEN, MediaLayout, MediaPayloadError, MediaSectionDescriptor,
MediaSectionKind, SectionScan,
},
};
pub(super) struct ImageSections<T> {
pub surface: Option<T>,
pub codings: Option<T>,
pub data: Option<T>,
pub records: Option<T>,
pub indexes: Option<T>,
pub color_table: Option<T>,
}
impl<T> Default for ImageSections<T> {
fn default() -> Self {
Self {
surface: None,
codings: None,
data: None,
records: None,
indexes: None,
color_table: None,
}
}
}
impl<T> ImageSections<T> {
pub fn accept(
&mut self,
value: T,
descriptor: MediaSectionDescriptor,
) -> Result<(), EncodedImageError> {
let kind = descriptor.kind();
let slot = match kind {
MediaSectionKind::SURFACE => Some(&mut self.surface),
MediaSectionKind::CODINGS => Some(&mut self.codings),
MediaSectionKind::DATA => Some(&mut self.data),
MediaSectionKind::UNIT_GROUPS => Some(&mut self.records),
MediaSectionKind::UNIT_INDEX => Some(&mut self.indexes),
MediaSectionKind::COLOR_TABLE => Some(&mut self.color_table),
MediaSectionKind::INTEGRITY => None,
MediaSectionKind::PLANES => return Err(EncodedImageError::UnexpectedSection(kind)),
_ if descriptor.flags().is_required() => {
return Err(EncodedImageError::UnknownRequiredSection(kind));
}
_ => None,
};
if let Some(slot) = slot {
if slot.is_some() {
return Err(EncodedImageError::DuplicateSection(kind));
}
if !descriptor.flags().is_required() {
return Err(EncodedImageError::SectionMustBeRequired(kind));
}
*slot = Some(value);
}
Ok(())
}
}
pub(super) fn parse_surface(bytes: &[u8]) -> Result<SurfaceDescriptor, EncodedImageError> {
check_size(MediaSectionKind::SURFACE, SURFACE_RECORD_LEN, bytes.len())?;
SurfaceDescriptor::from_record(bytes).map_err(EncodedImageError::Surface)
}
fn check_size(
kind: MediaSectionKind,
expected: usize,
actual: usize,
) -> Result<(), EncodedImageError> {
if actual == expected {
Ok(())
} else {
Err(EncodedImageError::SectionSizeMismatch {
kind,
expected,
actual,
})
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum Phase {
Directory,
Surface,
Done,
}
pub struct EncodedImageScan {
directory: SectionScan,
sections: ImageSections<MediaSectionDescriptor>,
record: [u8; MEDIA_SECTION_LEN],
surface: [u8; SURFACE_RECORD_LEN],
phase: Phase,
filled: usize,
payload_offset: u32,
}
impl EncodedImageScan {
pub fn new(layout: MediaLayout, payload_offset: u32) -> Self {
Self {
directory: layout.sections(),
sections: ImageSections::default(),
record: [0; MEDIA_SECTION_LEN],
surface: [0; SURFACE_RECORD_LEN],
phase: Phase::Directory,
filled: 0,
payload_offset,
}
}
pub fn read_range(&self) -> Option<Range<usize>> {
let mut range = match self.phase {
Phase::Directory => self.directory.record_range()?,
Phase::Surface => {
let section = self.sections.surface?;
section.offset() as usize..section.offset() as usize + section.size() as usize
}
Phase::Done => return None,
};
range.start += self.filled;
Some(range)
}
pub fn accept(&mut self, bytes: &[u8]) -> Result<(), EncodedImageError> {
let requested = self.read_range().ok_or_else(incomplete)?;
if bytes.is_empty() || bytes.len() > requested.len() {
return Err(incomplete());
}
let next = self.filled + bytes.len();
match self.phase {
Phase::Directory => {
self.record[self.filled..next].copy_from_slice(bytes);
if next == self.record.len() {
let mut directory = self.directory;
let descriptor = directory
.accept(&self.record)
.map_err(EncodedImageError::Media)?
.expect("requested record");
self.sections.accept(descriptor, descriptor)?;
self.directory = directory;
}
}
Phase::Surface => self.surface[self.filled..next].copy_from_slice(bytes),
Phase::Done => unreachable!(),
}
self.filled = next;
if bytes.len() == requested.len() {
self.filled = 0;
match self.phase {
Phase::Directory if self.directory.record_range().is_none() => {
let surface = self
.sections
.surface
.ok_or(EncodedImageError::MissingSection(MediaSectionKind::SURFACE))?;
check_size(
MediaSectionKind::SURFACE,
SURFACE_RECORD_LEN,
surface.size() as usize,
)?;
self.sections
.codings
.ok_or(EncodedImageError::MissingSection(MediaSectionKind::CODINGS))?;
self.sections
.data
.ok_or(EncodedImageError::MissingSection(MediaSectionKind::DATA))?;
self.phase = Phase::Surface;
}
Phase::Surface => {
parse_surface(&self.surface)?.validate_color_table_size(
self.sections.color_table.map(|s| s.size() as usize),
)?;
self.phase = Phase::Done;
}
_ => {}
}
}
Ok(())
}
pub fn section(&self, kind: MediaSectionKind) -> Option<MediaSectionDescriptor> {
if self.phase != Phase::Done {
return None;
}
match kind {
MediaSectionKind::SURFACE => self.sections.surface,
MediaSectionKind::CODINGS => self.sections.codings,
MediaSectionKind::DATA => self.sections.data,
MediaSectionKind::UNIT_GROUPS => self.sections.records,
MediaSectionKind::UNIT_INDEX => self.sections.indexes,
MediaSectionKind::COLOR_TABLE => self.sections.color_table,
_ => None,
}
}
pub fn surface(&self) -> Result<SurfaceDescriptor, EncodedImageError> {
if self.phase != Phase::Done {
return Err(incomplete());
}
parse_surface(&self.surface)
}
pub fn finish<'a>(
&self,
codings: &'a [u8],
groups: Option<&'a [u8]>,
indexes: Option<&'a [u8]>,
) -> Result<EncodedImageLayout<'a>, EncodedImageError> {
let surface = self.surface()?;
for (kind, expected, actual) in [
(
MediaSectionKind::CODINGS,
self.sections.codings,
Some(codings),
),
(MediaSectionKind::UNIT_GROUPS, self.sections.records, groups),
(MediaSectionKind::UNIT_INDEX, self.sections.indexes, indexes),
] {
match (expected, actual) {
(Some(section), Some(bytes)) => {
check_size(kind, section.size() as usize, bytes.len())?
}
(Some(_), None) => return Err(EncodedImageError::MissingSection(kind)),
(None, Some(_)) => return Err(EncodedImageError::UnexpectedSection(kind)),
(None, None) => {}
}
}
let data = self.sections.data.expect("checked DATA section");
EncodedImageLayout::new(
surface,
codings,
groups,
indexes,
data.size(),
Some(
self.payload_offset
.checked_add(data.offset())
.ok_or(EncodedImageError::SizeOverflow)?,
),
)
}
}
fn incomplete() -> EncodedImageError {
EncodedImageError::Media(MediaPayloadError::InvalidDataRange)
}
#[cfg(test)]
mod tests;