mirx 0.52.0

MIRX binary image format — flat & chunk-based container for ahead-of-time UI assets
Documentation
use alloc::vec::Vec;

use super::{ChunkNode, Document, DocumentState, EncodeError, FlatRecord, PayloadStorage};
use crate::image::{EncodedImageAsset, EncodedImageView, ImageRef, RawImageView, SurfaceView};
use crate::payload::image::{
    ImageAssetEncodeError, ImagePayloadError, ImagePayloadPlan, ImagePlanes,
};
use crate::{ChunkType, ImageView, PayloadLimits, PrimaryHints};

#[derive(Clone, Copy)]
pub(super) enum PayloadPlacement {
    Fixed(u32),
    Unplaced,
}

pub(super) type ResolvedImagePlanes<'a> = ImagePlanes<'a>;

#[derive(Clone, Copy)]
pub(super) enum ResolvedNodePayload<'a> {
    Contiguous {
        bytes: &'a [u8],
        placement: PayloadPlacement,
    },
    PromotedImage(ResolvedImagePlanes<'a>),
}

impl<'a> ResolvedNodePayload<'a> {
    pub(super) fn frames_view(
        self,
        limits: &PayloadLimits,
    ) -> Result<crate::frames::FramesView<'a>, crate::frames::FramesError> {
        match self {
            Self::Contiguous { bytes, placement } => match placement {
                PayloadPlacement::Fixed(offset) => {
                    crate::frames::FramesView::open_at(bytes, offset, limits)
                }
                PayloadPlacement::Unplaced => crate::frames::FramesView::open(bytes, limits),
            },
            Self::PromotedImage(_) => Err(crate::frames::FramesError::SizeOverflow),
        }
    }

    pub(super) fn font_view(
        self,
        limits: &PayloadLimits,
    ) -> Result<crate::font::FontView<'a>, crate::font::FontError> {
        match self {
            Self::Contiguous { bytes, placement } => match placement {
                PayloadPlacement::Fixed(offset) => {
                    crate::font::FontView::open_at(bytes, offset, limits)
                }
                PayloadPlacement::Unplaced => crate::font::FontView::open(bytes, limits),
            },
            Self::PromotedImage(_) => Err(crate::font::FontError::SizeOverflow),
        }
    }
    pub(super) const fn bytes(self) -> Option<&'a [u8]> {
        match self {
            Self::Contiguous { bytes, .. } => Some(bytes),
            Self::PromotedImage(_) => None,
        }
    }

    pub(super) fn encoded_len(self) -> Result<usize, EncodeError> {
        match self {
            Self::Contiguous { bytes, .. } => Ok(bytes.len()),
            Self::PromotedImage(image) => Ok(plan_image_payload(image)?.encoded_len()),
        }
    }

    pub(super) fn copy_into(self, out: &mut [u8]) -> Result<usize, EncodeError> {
        match self {
            Self::Contiguous { bytes, .. } => {
                if out.len() < bytes.len() {
                    return Err(EncodeError::BufferTooSmall {
                        needed: bytes.len(),
                        available: out.len(),
                    });
                }
                out[..bytes.len()].copy_from_slice(bytes);
                Ok(bytes.len())
            }
            Self::PromotedImage(image) => plan_image_payload(image)?
                .copy_payload_into(out)
                .map_err(encode_error_for_image_encoding),
        }
    }

    pub(super) fn to_vec(self) -> Result<Vec<u8>, EncodeError> {
        match self {
            Self::Contiguous { bytes, .. } => {
                let mut out = Vec::new();
                out.try_reserve_exact(bytes.len())
                    .map_err(|_| EncodeError::AllocationFailed)?;
                out.extend_from_slice(bytes);
                Ok(out)
            }
            Self::PromotedImage(image) => plan_image_payload(image)?
                .payload_to_vec()
                .map_err(encode_error_for_image_encoding),
        }
    }

    pub(super) fn equals(self, candidate: &[u8]) -> bool {
        match self {
            Self::Contiguous { bytes, .. } => bytes == candidate,
            Self::PromotedImage(image) => {
                plan_image_payload(image).is_ok_and(|plan| plan.equals_payload(candidate))
            }
        }
    }

    pub(super) fn validate_image_contract(
        self,
        limits: &PayloadLimits,
    ) -> Result<PrimaryHints, ImagePayloadError> {
        let view = self.image_view()?;
        let stride = match view {
            ImageRef::Raw(raw) => raw
                .plane(0)
                .expect("validated main plane")
                .memory()
                .stride(),
            ImageRef::Encoded(encoded) => {
                encoded
                    .preflight(limits)
                    .map_err(ImagePayloadError::Encoded)?;
                0
            }
        };
        let surface = view.surface();
        Ok(PrimaryHints::new(
            surface.sample_layout(),
            surface.width(),
            surface.height(),
            stride,
        ))
    }

    pub(super) fn image_view(self) -> Result<ImageRef<'a>, ImagePayloadError> {
        match self {
            Self::Contiguous { bytes, placement } => match placement {
                PayloadPlacement::Fixed(offset) => ImageRef::open_at(bytes, offset),
                PayloadPlacement::Unplaced => ImageRef::open(bytes),
            }
            .map_err(Into::into),
            Self::PromotedImage(image) => {
                ImagePayloadPlan::from_planes(image).map(|plan| ImageRef::Raw(plan.surface()))
            }
        }
    }

    pub(super) fn equals_surface(self, candidate: SurfaceView<'_>) -> bool {
        match self {
            Self::Contiguous { bytes, placement } => {
                let aligned = match placement {
                    PayloadPlacement::Fixed(_) => self.image_view().is_ok(),
                    PayloadPlacement::Unplaced => true,
                };
                aligned && candidate.matches_payload(bytes).unwrap_or(false)
            }
            Self::PromotedImage(_) => self
                .image_view()
                .is_ok_and(|existing| existing.raw() == Some(candidate)),
        }
    }

    pub(super) fn equals_encoded(
        self,
        candidate: EncodedImageAsset<'_>,
    ) -> Result<bool, crate::image::ImageEncodeError> {
        let Self::Contiguous { bytes, placement } = self else {
            return Ok(false);
        };
        if !candidate.matches_payload(bytes)? {
            return Ok(false);
        }
        Ok(match placement {
            PayloadPlacement::Unplaced => true,
            PayloadPlacement::Fixed(offset) => EncodedImageView::open(bytes).is_ok_and(|image| {
                image.input_alignment().is_ok_and(|alignment| {
                    image
                        .media()
                        .validate_file_alignment(offset, alignment)
                        .is_ok()
                })
            }),
        })
    }

    pub(super) fn image_planes(self) -> Result<ResolvedImagePlanes<'a>, ImagePayloadError> {
        let packed = match self {
            Self::Contiguous { bytes, placement } => {
                let raw = match placement {
                    PayloadPlacement::Fixed(offset) => RawImageView::open_at(bytes, offset),
                    PayloadPlacement::Unplaced => RawImageView::open(bytes),
                }
                .map_err(|error| match error {
                    crate::image::RawImageViewError::UnexpectedSection(
                        crate::media::MediaSectionKind::CODINGS,
                    ) => ImagePayloadError::NotRepresentableAsPacked,
                    error => ImagePayloadError::Media(error),
                })?;
                raw.packed()
            }
            Self::PromotedImage(_) => self.image_view()?.raw().and_then(|raw| raw.packed()),
        }
        .ok_or(ImagePayloadError::NotRepresentableAsPacked)?;
        resolved_image_planes(packed)
    }
}

fn resolved_image_planes(
    image: ImageView<'_>,
) -> Result<ResolvedImagePlanes<'_>, ImagePayloadError> {
    ImagePlanes::from_view(image)
}

pub(super) fn resolve_flat_record<'document>(
    document: &'document Document<'_>,
    record: &'document FlatRecord<'_>,
) -> Result<ResolvedImagePlanes<'document>, ImagePayloadError> {
    let (main, extra) = record
        .resolve_planes(&document.origin)
        .ok_or(ImagePayloadError::SizeOverflow)?;
    ImagePlanes::new(record.image, main, extra)
}

pub(super) fn resolve_node_payload<'document>(
    document: &'document Document<'_>,
    node: &'document ChunkNode<'_>,
) -> Result<ResolvedNodePayload<'document>, ImagePayloadError> {
    match &node.payload {
        PayloadStorage::SourceRange(range) => Ok(ResolvedNodePayload::Contiguous {
            bytes: document
                .origin
                .resolve(*range)
                .ok_or(ImagePayloadError::SizeOverflow)?,
            placement: PayloadPlacement::Fixed(
                u32::try_from(range.start()).map_err(|_| ImagePayloadError::SizeOverflow)?,
            ),
        }),
        PayloadStorage::Borrowed(bytes) => Ok(ResolvedNodePayload::Contiguous {
            bytes,
            placement: PayloadPlacement::Unplaced,
        }),
        PayloadStorage::Owned(bytes) => Ok(ResolvedNodePayload::Contiguous {
            bytes: bytes.as_slice(),
            placement: PayloadPlacement::Unplaced,
        }),
        PayloadStorage::PromotedFlat => {
            let DocumentState::Chunk(chunks) = &document.state else {
                unreachable!("promoted payload requires CHUNK state");
            };
            let record = chunks
                .promoted_flat
                .as_ref()
                .expect("promoted payload tag requires its FLAT sidecar");
            resolve_flat_record(document, record).map(ResolvedNodePayload::PromotedImage)
        }
    }
}

pub(super) fn encode_error_for_image(_: ImagePayloadError) -> EncodeError {
    EncodeError::InvalidPayload {
        chunk_type: ChunkType::IMAGE,
    }
}

fn plan_image_payload(image: ResolvedImagePlanes<'_>) -> Result<ImagePayloadPlan<'_>, EncodeError> {
    ImagePayloadPlan::from_planes(image).map_err(|error| match error {
        ImagePayloadError::SizeOverflow
        | ImagePayloadError::Surface(crate::image::ImageEncodeError::SizeOverflow) => {
            EncodeError::SizeOverflow
        }
        _ => EncodeError::InvalidPayload {
            chunk_type: ChunkType::IMAGE,
        },
    })
}

fn encode_error_for_image_encoding(error: ImageAssetEncodeError) -> EncodeError {
    match error {
        ImageAssetEncodeError::InvalidPayload(_) => EncodeError::InvalidPayload {
            chunk_type: ChunkType::IMAGE,
        },
        ImageAssetEncodeError::BufferTooSmall { needed, available } => {
            EncodeError::BufferTooSmall { needed, available }
        }
        ImageAssetEncodeError::AllocationFailed => EncodeError::AllocationFailed,
    }
}