LR-IMP-FORMATS: ARW6 container rejection tests and tile byte ranges

This commit is contained in:
dev-null-undefined committed 2026-10-10 21:07:29 +02:00
1 parent f101a09142
commit e45c4625b4
1 file changed
+84
+84
View File
@@ -91,6 +91,16 @@ pub(crate) fn parse_tile_table(strip: &[u8], width: usize, height: usize) -> Res
Ok(tiles)
}
/// Tile `i`'s bytes: from its offset to the next tile's offset (the last: the strip end).
pub(crate) fn tile_bytes<'a>(strip: &'a [u8], tiles: &[TileEntry], i: usize) -> Option<&'a [u8]> {
let start = tiles.get(i)?.offset;
let end = match tiles.get(i.checked_add(1)?) {
Some(next) => next.offset,
None => strip.len(),
};
strip.get(start..end)
}
/// One of a tile's 13 streams (positions in 16-byte words from the tile start).
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct StreamRef {
@@ -294,6 +304,80 @@ mod tests {
assert!(parse_tile_header(&tile, 64, 50).is_err()); // VS != th / 2
}
fn put32(b: &mut [u8], at: usize, v: u32) {
b[at..at + 4].copy_from_slice(&v.to_le_bytes());
}
#[test]
fn tile_table_validation_cases() {
let (tile, _) = one_tile(64, 48, 0, Qis::ZERO);
let many = |n: usize| {
let tiles: Vec<_> = (0..n).map(|i| (64 * i, 0, 64, 48, tile.clone())).collect();
strip_of(&arw6_file(&tiles, 64 * n, 48)).to_vec()
};
let sixteen = many(16);
assert_eq!(parse_tile_table(&sixteen, 1024, 48).unwrap().len(), 16);
let mut seventeen = sixteen.clone();
seventeen[0] = 17;
assert!(parse_tile_table(&seventeen, 1088, 48).is_err());
let two = strip_of(&arw6_file(&[(0, 0, 64, 48, tile.clone()), (64, 0, 64, 48, tile.clone())], 128, 48)).to_vec();
assert_eq!(parse_tile_table(&two, 128, 48).unwrap().len(), 2);
let mut swapped = two.clone();
let (a, b) = (two[8..16].to_vec(), two[32..40].to_vec());
swapped[8..16].copy_from_slice(&b);
swapped[32..40].copy_from_slice(&a);
assert!(parse_tile_table(&swapped, 128, 48).is_err());
let mut at_end = two.clone();
at_end[32..40].copy_from_slice(&(two.len() as u64).to_le_bytes());
assert!(parse_tile_table(&at_end, 128, 48).is_err());
let one = many(1);
let mut wide = one.clone();
put32(&mut wide, 24, 72); // w / 2 = 36 is not a multiple of 8
assert!(parse_tile_table(&wide, 72, 48).is_err());
let mut huge = one.clone();
put32(&mut huge, 24, 1 << 20);
put32(&mut huge, 28, 1 << 20);
assert!(matches!(parse_tile_table(&huge, 1 << 20, 1 << 20), Err(RawError::Limit(_))));
}
#[test]
fn tile_bytes_span_to_the_next_offset() {
let (tile, _) = one_tile(64, 48, 0, Qis::ZERO);
let file = arw6_file(&[(0, 0, 64, 48, tile.clone()), (64, 0, 64, 48, tile.clone())], 128, 48);
let strip = strip_of(&file);
let tiles = parse_tile_table(strip, 128, 48).unwrap();
let (t0, t1) = (tile_bytes(strip, &tiles, 0).unwrap(), tile_bytes(strip, &tiles, 1).unwrap());
assert_eq!(t0.len(), tiles[1].offset - tiles[0].offset);
assert_eq!(t0, &tile[..]);
assert_eq!(t1.as_ptr() as usize + t1.len(), strip.as_ptr() as usize + strip.len());
assert!(tile_bytes(strip, &tiles, 2).is_none());
}
#[test]
fn tile_header_and_stream_validation_cases() {
let (tile, _) = one_tile(64, 48, 0, Qis::REAL);
let h = parse_tile_header(&tile, 64, 48).unwrap();
let mut two_streams = tile.clone();
two_streams[3 * 16] = 2;
assert!(parse_tile_header(&two_streams, 64, 48).is_err());
let mut big = tile.clone();
big[3 * 16 + 1..3 * 16 + 4].copy_from_slice(&[0xff; 3]); // stream 0 larger than the tile
assert!(parse_tile_header(&big, 64, 48).is_err());
let mut idx = tile.clone();
idx[8 * 16..8 * 16 + 2].copy_from_slice(&[0xff, 0xff]); // index words beyond the stream
assert!(parse_tile_header(&idx, 64, 48).is_err());
let s = &h.streams[9];
let mut bad_half = tile.clone();
let idx_start = (s.word + 1) * 16;
bad_half[idx_start..idx_start + 2].copy_from_slice(&[0xff, 0xff]); // A past the data area
assert!(stream_halves(&bad_half, s, h.ntu).is_err());
let outside = StreamRef { index_words: 0xffff, ..s.clone() };
assert!(stream_halves(&tile, &outside, h.ntu).is_err());
}
#[test]
fn every_stream_of_an_encoded_tile_parses() {
for s in [0u8, 3] {