mirx 0.43.0

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

fn wire_fixed(bits: i32) -> Fixed {
    Fixed::from_le_bytes(bits.to_le_bytes())
}

#[test]
fn signed_fixed_records_preserve_precision_and_declared_line_spacing() {
    let glyph = GlyphMetrics::new(wire_fixed(-1), wire_fixed(i32::MIN), wire_fixed(i32::MAX));
    let mut bytes = [0x5a; GLYPH_METRICS_LEN + 1];
    assert_eq!(glyph.encode_record_into(&mut bytes), Ok(GLYPH_METRICS_LEN));
    assert_eq!(
        bytes,
        [255, 255, 255, 255, 0, 0, 0, 128, 255, 255, 255, 127, 0x5a]
    );
    assert_eq!(GlyphMetrics::from_record(&bytes), Ok(glyph));
    assert_eq!(glyph.advance(), wire_fixed(-1));
    assert_eq!(glyph.bearing_x(), wire_fixed(i32::MIN));
    assert_eq!(glyph.bearing_y(), wire_fixed(i32::MAX));
    for values in [[0, 0, 1], [i32::MAX, i32::MIN, 1], [2560, -768, 4096]] {
        let line = LineMetrics::new(
            wire_fixed(values[0]),
            wire_fixed(values[1]),
            wire_fixed(values[2]),
        )
        .unwrap();
        assert_eq!(line.ascent(), wire_fixed(values[0]));
        assert_eq!(line.descent(), wire_fixed(values[1]));
        assert_eq!(line.line_height(), wire_fixed(values[2]));
        let mut bytes = [0x5a; LINE_METRICS_LEN + 1];
        line.encode_record_into(&mut bytes).unwrap();
        for (i, field) in values.into_iter().enumerate() {
            assert_eq!(&bytes[i * 4..i * 4 + 4], field.to_le_bytes());
        }
        assert_eq!(bytes[LINE_METRICS_LEN], 0x5a);
        assert_eq!(LineMetrics::from_record(&bytes), Ok(line));
    }
}

#[test]
fn validation_precedes_writes_and_accepts_only_complete_tables() {
    let valid = LineMetrics::new(wire_fixed(10), wire_fixed(-3), wire_fixed(12)).unwrap();
    for (values, error) in [
        ([-1, 0, 1], MetricsError::NegativeAscent(wire_fixed(-1))),
        ([1, 1, 1], MetricsError::PositiveDescent(wire_fixed(1))),
        (
            [0, 0, 0],
            MetricsError::NonPositiveLineHeight(wire_fixed(0)),
        ),
        (
            [1, -1, -1],
            MetricsError::NonPositiveLineHeight(wire_fixed(-1)),
        ),
    ] {
        let mut bytes = [0; LINE_METRICS_LEN];
        for (i, field) in values.into_iter().enumerate() {
            bytes[i * 4..i * 4 + 4].copy_from_slice(&i32::to_le_bytes(field));
        }
        assert_eq!(
            LineMetrics::new(
                wire_fixed(values[0]),
                wire_fixed(values[1]),
                wire_fixed(values[2])
            ),
            Err(error)
        );
        assert_eq!(LineMetrics::from_record(&bytes), Err(error));
        assert_eq!(MetricsTable::open(&bytes), Err(error));
    }
    for size in 0..LINE_METRICS_LEN {
        let mut bytes = [0x5a; LINE_METRICS_LEN];
        assert_eq!(
            valid.encode_record_into(&mut bytes[..size]),
            Err(MetricsError::BufferTooSmall {
                needed: LINE_METRICS_LEN,
                available: size,
            })
        );
        assert!(
            GlyphMetrics::default()
                .encode_record_into(&mut bytes[..size])
                .is_err()
        );
        assert_eq!(bytes, [0x5a; LINE_METRICS_LEN]);
        assert!(LineMetrics::from_record(&bytes[..size]).is_err());
        assert!(GlyphMetrics::from_record(&bytes[..size]).is_err());
        assert!(MetricsTable::open(&bytes[..size]).is_err());
    }
    let mut table = [0; LINE_METRICS_LEN + GLYPH_METRICS_LEN];
    valid.encode_record_into(&mut table).unwrap();
    for size in LINE_METRICS_LEN + 1..table.len() {
        assert_eq!(
            MetricsTable::open(&table[..size]),
            Err(MetricsError::PartialRecord { byte_len: size })
        );
    }
    assert!(
        MetricsTable::open(&table[..LINE_METRICS_LEN])
            .unwrap()
            .is_empty()
    );
}

#[test]
fn unaligned_tables_share_ordinals_and_read_without_scanning() {
    #[repr(align(4))]
    struct Bytes([u8; 1 + LINE_METRICS_LEN + 6 * GLYPH_METRICS_LEN]);
    let line = LineMetrics::new(wire_fixed(100), wire_fixed(-20), wire_fixed(150)).unwrap();
    let mut bytes = Bytes([0; 1 + LINE_METRICS_LEN + 6 * GLYPH_METRICS_LEN]);
    line.encode_record_into(&mut bytes.0[1..]).unwrap();
    let mut glyphs = [GlyphMetrics::default(); 6];
    for (index, glyph) in glyphs.iter_mut().enumerate() {
        *glyph = GlyphMetrics::new(
            wire_fixed(index as i32),
            wire_fixed(-1000),
            wire_fixed(90000),
        );
        glyph
            .encode_record_into(&mut bytes.0[1 + LINE_METRICS_LEN + index * GLYPH_METRICS_LEN..])
            .unwrap();
    }
    let table = MetricsTable::open(&bytes.0[1..]).unwrap();
    assert_eq!(table.as_bytes().as_ptr() as usize % 4, 1);
    assert_eq!(table.as_bytes().as_ptr(), bytes.0[1..].as_ptr());
    assert_eq!(table.line_metrics(), line);
    assert_eq!(table.len(), glyphs.len());
    assert!(!table.is_empty());
    assert_eq!(table.iter().count(), glyphs.len());
    assert_eq!(table.iter().last(), Some(glyphs[5]));
    for (index, glyph) in glyphs.into_iter().enumerate() {
        assert_eq!(table.get(index), Some(glyph));
    }
    for index in [glyphs.len(), usize::MAX, usize::MAX / GLYPH_METRICS_LEN] {
        assert_eq!(table.get(index), None);
    }
    let mut iter = table.into_iter();
    assert_eq!(iter.next(), Some(glyphs[0]));
    assert_eq!(iter.next_back(), Some(glyphs[5]));
    assert_eq!(iter.nth(1), Some(glyphs[2]));
    assert_eq!(iter.nth_back(1), Some(glyphs[3]));
    assert_eq!(iter.len(), 0);
    assert_eq!(iter.next(), None);
    assert_eq!(iter.next_back(), None);
    for reverse in [false, true] {
        let mut iter = table.iter();
        assert_eq!(
            if reverse {
                iter.nth_back(usize::MAX)
            } else {
                iter.nth(usize::MAX)
            },
            None
        );
        assert_eq!(iter.len(), 0);
        assert_eq!(iter.next(), None);
        assert_eq!(iter.next_back(), None);
    }
}