j2k-native 0.13.0

Pure-Rust JPEG 2000 and HTJ2K codec engine for j2k
Documentation
// SPDX-License-Identifier: MIT OR Apache-2.0

use super::{
    flat_magsgn_capacity, unstuff_magsgn, FlatBitReader, ForwardBitReader, MelDecoder,
    ReverseBitReader,
};

#[test]
fn reader_state_and_bit_consumption_match_pre_split_goldens() {
    let data = [0xAA, 0xFF, 0x01, 0x7F, 0x80];
    let mut forward = ForwardBitReader::<0xFF>::new(&data);
    assert_eq!(u64::from(forward.fetch()), 0x3F81_FFAA);
    forward.advance(5);
    assert_eq!(u64::from(forward.fetch()), 0x01FC_0FFD);
    forward.advance(19);
    assert_eq!(u64::from(forward.fetch()), 0xFFFF_C03F);
    assert_eq!(
        (forward.pos, forward.bits, forward.tmp, forward.unstuff),
        (5, 37, 0x0000_001F_FFFF_C03F, true)
    );

    let mut reverse = ReverseBitReader::new_mrp(&data);
    assert_eq!(u64::from(reverse.fetch()), 0xFF01_7F80);
    assert_eq!(reverse.advance(7), 0x55FE_02FF);
    assert_eq!(u64::from(reverse.fetch()), 0x55FE_02FF);
    assert_eq!(
        (
            reverse.pos,
            reverse.remaining,
            reverse.bits,
            reverse.tmp,
            reverse.unstuff,
        ),
        (-1, 0, 33, 0x0000_0001_55FE_02FF, true)
    );

    // MEL runs (zero events << 1 | terminated) across stuffed bytes, the
    // marked last byte, and the 0xFF bytes after it, as the bit-serial
    // decoder read them.
    let mel_cases: [(&[u8], usize, [i32; 24]); 4] = [
        (
            &[0x12, 0x34, 0x56, 0x78],
            2,
            [
                1, 0, 1, 0, 0, 0, 2, 2, 2, 6, 6, 6, 14, 14, 30, 62, 62, 62, 62, 62, 62, 62, 62, 62,
            ],
        ),
        (
            &[0xFF, 0x80, 0xFF, 0xFF, 0x3C, 0x00, 0x00],
            7,
            [
                0, 0, 0, 2, 2, 2, 6, 6, 1, 1, 7, 2, 6, 6, 6, 14, 14, 30, 62, 62, 62, 62, 62, 62,
            ],
        ),
        (&[0x00; 6], 6, [1; 24]),
        (
            &[0xFF, 0xFF, 0xFF, 0xFF, 0x5A, 0xA5],
            6,
            [
                0, 0, 0, 2, 2, 2, 6, 6, 6, 14, 14, 30, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
                62,
            ],
        ),
    ];
    for (mel_data, scup, expected) in mel_cases {
        let mut mel = MelDecoder::new(mel_data, mel_data.len(), scup);
        let runs = expected.map(|_| mel.get_run().expect("MEL run"));
        assert_eq!(runs, expected, "MEL data {mel_data:02X?}");
    }

    let vlc_data = [0x12, 0x34, 0x56, 0x78, 0x9A];
    let mut vlc = ReverseBitReader::new_vlc(&vlc_data, vlc_data.len(), 3);
    assert_eq!(vlc.fetch(), 0x0000_02B7);
    assert_eq!(vlc.advance(9), 0x0000_0001);
    assert_eq!(vlc.fetch(), 0x0000_0001);
    assert_eq!(
        (vlc.pos, vlc.remaining, vlc.bits, vlc.tmp, vlc.unstuff),
        (1, 0, 34, 1, false)
    );
}

#[test]
fn word_refills_yield_the_same_bits_as_byte_refills() {
    let mut seed = 0x1234_5678_u32;
    let mut next = move || {
        seed ^= seed << 13;
        seed ^= seed >> 17;
        seed ^= seed << 5;
        seed
    };
    // Bytes around the stuffing thresholds are over-represented.
    let special = [0xFF_u8, 0x7F, 0x8F, 0x90, 0x00, 0xFE];
    for len in 0..=40_usize {
        for _ in 0..20 {
            let data = (0..len)
                .map(|_| {
                    let draw = next();
                    if draw % 3 == 0 {
                        special[(draw >> 8) as usize % special.len()]
                    } else {
                        (draw >> 16).to_le_bytes()[0]
                    }
                })
                .collect::<Vec<_>>();
            let advances = (0..24).map(|_| next() % 33).collect::<Vec<_>>();

            let mut forward = ForwardBitReader::<0xFF>::new(&data);
            let mut forward_bytes = forward.clone();
            let mut reverse = ReverseBitReader::new_mrp(&data);
            let mut reverse_bytes = reverse.clone();
            for &count in &advances {
                if forward_bytes.bits < 32 {
                    forward_bytes = forward_bytes.fill_bytes();
                }
                assert_eq!(
                    u64::from(forward.fetch()),
                    forward_bytes.tmp & u64::from(u32::MAX),
                    "forward len {len}"
                );
                forward.advance(count);
                forward_bytes.advance(count);

                if reverse_bytes.bits < 32 {
                    reverse_bytes = reverse_bytes.fill_bytes();
                }
                assert_eq!(
                    u64::from(reverse.fetch()),
                    reverse_bytes.tmp & u64::from(u32::MAX),
                    "reverse len {len}"
                );
                reverse.advance(count);
                reverse_bytes.advance(count);
            }
        }
    }
}

#[test]
fn flat_magsgn_reader_matches_reservoir_reader() {
    let mut state = 0x2545_F491_4F6C_DD1D_u64;
    let mut next = move || {
        state ^= state << 13;
        state ^= state >> 7;
        state ^= state << 17;
        state
    };
    for len in 0..300_usize {
        for density in [0_u64, 4, 32, 200] {
            let data = (0..len)
                .map(|_| {
                    let draw = next();
                    if draw % 256 < density {
                        0xFF
                    } else {
                        draw.to_le_bytes()[3]
                    }
                })
                .collect::<Vec<_>>();
            // Enough advances to run far past the end of the segment.
            let advances = (0..(len / 2 + 24))
                .map(|_| (next() % 33) as u32)
                .collect::<Vec<_>>();
            let samples = advances.len();
            let mut buffer = Vec::with_capacity(flat_magsgn_capacity(samples).expect("capacity"));
            let flat = unstuff_magsgn(&data, &mut buffer, samples * 4).expect("unstuffed");
            let mut flat = FlatBitReader::new(flat).expect("flat reader");
            let mut reservoir = ForwardBitReader::<0xFF>::new(&data);
            for (step, &count) in advances.iter().enumerate() {
                assert_eq!(
                    flat.fetch(),
                    reservoir.fetch(),
                    "len {len} density {density} step {step}"
                );
                flat.advance(count);
                reservoir.advance(count);
            }
        }
    }
}

#[test]
fn flat_magsgn_unstuffing_requires_reserved_capacity() {
    let mut buffer = Vec::new();
    assert!(unstuff_magsgn(&[1, 2, 3], &mut buffer, 4).is_none());
    assert_eq!(buffer.capacity(), 0, "unstuffing must not allocate");

    let capacity = flat_magsgn_capacity(1).expect("one-sample capacity");
    let mut buffer = Vec::with_capacity(capacity);
    let reserved = buffer.capacity();
    let flat = unstuff_magsgn(&[0; 8], &mut buffer, 4).expect("long segment");
    assert_eq!(&flat[..4], &[0; 4]);
    assert!(
        flat.len() <= capacity,
        "segment padding must fit its reservation"
    );
    assert_eq!(
        buffer.capacity(),
        reserved,
        "long segments must not allocate"
    );
}