mirx 0.45.0

MIRX binary image format — flat & chunk-based container for ahead-of-time UI assets
Documentation
use crate::{
    crc32::Crc32,
    media::{
        DataIntegrity, MEDIA_HEADER_LEN, MEDIA_SECTION_LEN, MediaSectionFlags, MediaSectionKind,
    },
    wire::{write_u16_le, write_u32_le},
};

enum Destination<'a> {
    Buffer(&'a mut [u8]),
    Comparison { bytes: &'a [u8], equal: bool },
}

enum ChecksumScope {
    Metadata,
    WholeData,
    IndexedData,
}

pub(crate) struct PayloadOutput<'a> {
    destination: Destination<'a>,
    cursor: usize,
    crc: Crc32,
    metadata_crc: Crc32,
    scope: ChecksumScope,
    indexed_integrity: bool,
}

impl<'a> PayloadOutput<'a> {
    pub(crate) fn buffer(bytes: &'a mut [u8]) -> Self {
        Self {
            destination: Destination::Buffer(bytes),
            cursor: 0,
            crc: Crc32::new(),
            metadata_crc: Crc32::new(),
            scope: ChecksumScope::Metadata,
            indexed_integrity: false,
        }
    }

    pub(crate) fn comparison(bytes: &'a [u8]) -> Self {
        Self {
            destination: Destination::Comparison { bytes, equal: true },
            cursor: 0,
            crc: Crc32::new(),
            metadata_crc: Crc32::new(),
            scope: ChecksumScope::Metadata,
            indexed_integrity: false,
        }
    }

    pub(crate) fn header(&mut self, bytes: &[u8; MEDIA_HEADER_LEN]) {
        self.write(&bytes[..4]);
        self.cursor = MEDIA_HEADER_LEN;
    }

    pub(crate) fn position(&self) -> usize {
        self.cursor
    }

    pub(crate) fn begin_metadata(&mut self) {
        self.scope = ChecksumScope::Metadata;
    }

    pub(crate) fn begin_data(&mut self, integrity: DataIntegrity<'_>) {
        self.indexed_integrity = integrity.partitions().is_some();
        self.scope = match integrity {
            DataIntegrity::Whole => ChecksumScope::WholeData,
            DataIntegrity::Indexed(_) => ChecksumScope::IndexedData,
        };
    }

    fn write_at(&mut self, offset: usize, bytes: &[u8]) {
        let end = offset + bytes.len();
        match &mut self.destination {
            Destination::Buffer(out) => out[offset..end].copy_from_slice(bytes),
            Destination::Comparison {
                bytes: candidate,
                equal,
            } => {
                *equal &= candidate[offset..end] == *bytes;
            }
        }
    }

    pub(crate) fn write(&mut self, bytes: &[u8]) {
        self.write_at(self.cursor, bytes);
        match self.scope {
            ChecksumScope::WholeData => self.crc.update(bytes),
            ChecksumScope::Metadata => self.metadata_crc.update(bytes),
            ChecksumScope::IndexedData => {}
        }
        self.cursor += bytes.len();
    }

    pub(crate) fn pad_to(&mut self, offset: usize) {
        debug_assert!(self.cursor <= offset);
        const ZERO: [u8; 64] = [0; 64];
        while self.cursor < offset {
            self.write(&ZERO[..(offset - self.cursor).min(ZERO.len())]);
        }
    }

    pub(crate) fn section(&mut self, kind: MediaSectionKind, offset: usize, size: usize) {
        let mut entry = [0; MEDIA_SECTION_LEN];
        write_u16_le(&mut entry, 0, kind.raw());
        write_u16_le(&mut entry, 2, MediaSectionFlags::REQUIRED.bits());
        write_u32_le(&mut entry, 4, offset as u32);
        write_u32_le(&mut entry, 8, size as u32);
        self.write(&entry);
    }

    pub(crate) fn finish(mut self) -> bool {
        if !self.indexed_integrity {
            let crc = core::mem::replace(&mut self.crc, Crc32::new()).finish();
            self.begin_metadata();
            self.write(&crc.to_le_bytes());
        }
        let metadata_crc = core::mem::replace(&mut self.metadata_crc, Crc32::new()).finish();
        self.write_at(4, &metadata_crc.to_le_bytes());
        match self.destination {
            Destination::Buffer(_) => true,
            Destination::Comparison { equal, .. } => equal,
        }
    }
}