mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-27 11:37:13 -06:00
File diff suppressed because it is too large
Load Diff
@@ -169,3 +169,142 @@ fn non_finite_black_levels_and_samples_develop() {
|
||||
assert!(rgb.data.iter().flatten().all(|v| v.is_finite()), "float NaN samples: finite output");
|
||||
}
|
||||
}
|
||||
|
||||
// --- CR3 containers (Canon CRX): structurally valid files whose every byte may be mutated ---
|
||||
|
||||
/// A CR3 with a single CRAW track carrying a CMP1 descriptor (and optional CDI1/IAD1) plus raw samples.
|
||||
mod cr3 {
|
||||
pub(crate) fn box_bytes(kind: &[u8; 4], body: &[u8]) -> Vec<u8> {
|
||||
let mut out = ((body.len() + 8) as u32).to_be_bytes().to_vec();
|
||||
out.extend_from_slice(kind);
|
||||
out.extend_from_slice(body);
|
||||
out
|
||||
}
|
||||
|
||||
fn track(entry: &[u8], offset: u64, size: u32) -> Vec<u8> {
|
||||
let mut stsd = vec![0u8; 4];
|
||||
stsd.extend(1u32.to_be_bytes());
|
||||
stsd.extend_from_slice(entry);
|
||||
let mut stsz = vec![0u8; 4];
|
||||
stsz.extend(size.to_be_bytes());
|
||||
stsz.extend(1u32.to_be_bytes());
|
||||
let mut co64 = vec![0u8; 4];
|
||||
co64.extend(1u32.to_be_bytes());
|
||||
co64.extend(offset.to_be_bytes());
|
||||
let tables = [box_bytes(b"stsd", &stsd), box_bytes(b"stsz", &stsz), box_bytes(b"co64", &co64)].concat();
|
||||
box_bytes(b"trak", &box_bytes(b"mdia", &box_bytes(b"minf", &box_bytes(b"stbl", &tables))))
|
||||
}
|
||||
|
||||
/// `CMP1` coding descriptor: 52 bytes, four planes, 14 bits, RGGB, `levels` wavelet levels.
|
||||
pub(crate) fn cmp1(width: u32, height: u32, tile_width: u32, tile_height: u32, levels: u8, header_size: u32) -> Vec<u8> {
|
||||
let mut coding = vec![0u8; 52];
|
||||
coding[0..2].copy_from_slice(&0xff00u16.to_be_bytes());
|
||||
coding[2..4].copy_from_slice(&48u16.to_be_bytes());
|
||||
coding[4..6].copy_from_slice(&0x100u16.to_be_bytes());
|
||||
coding[8..12].copy_from_slice(&width.to_be_bytes());
|
||||
coding[12..16].copy_from_slice(&height.to_be_bytes());
|
||||
coding[16..20].copy_from_slice(&tile_width.to_be_bytes());
|
||||
coding[20..24].copy_from_slice(&tile_height.to_be_bytes());
|
||||
coding[24] = 14;
|
||||
coding[25] = 0x40;
|
||||
coding[26] = levels;
|
||||
coding[28..32].copy_from_slice(&header_size.to_be_bytes());
|
||||
coding
|
||||
}
|
||||
|
||||
pub(crate) fn file(coding: &[u8], width: u16, height: u16, sample: &[u8]) -> Vec<u8> {
|
||||
let mut craw = vec![0u8; 82];
|
||||
craw[24..26].copy_from_slice(&width.to_be_bytes());
|
||||
craw[26..28].copy_from_slice(&height.to_be_bytes());
|
||||
craw.extend(box_bytes(b"CMP1", coding));
|
||||
let entry = box_bytes(b"CRAW", &craw);
|
||||
let ftyp = box_bytes(b"ftyp", b"crx \0\0\0\x01crx isom");
|
||||
let track_len = track(&entry, 0, sample.len() as u32).len();
|
||||
let offset = (ftyp.len() + 8 + track_len + 7) & !7;
|
||||
let mut out = ftyp;
|
||||
out.extend(box_bytes(b"moov", &track(&entry, offset as u64, sample.len() as u32)));
|
||||
out.resize(offset, 0);
|
||||
out.extend_from_slice(sample);
|
||||
out
|
||||
}
|
||||
|
||||
fn marker(out: &mut Vec<u8>, code: u16, size: u32, flags: u32) {
|
||||
out.extend(code.to_be_bytes());
|
||||
out.extend(8u16.to_be_bytes());
|
||||
out.extend(size.to_be_bytes());
|
||||
out.extend(flags.to_be_bytes());
|
||||
}
|
||||
|
||||
/// A 4×4 lossless CRX stream: four 2×2 planes, each sample value 1 (sensor level 8193).
|
||||
pub(crate) fn lossless_sample() -> Vec<u8> {
|
||||
let mut sample = Vec::new();
|
||||
marker(&mut sample, 0xff01, 4, 0);
|
||||
for p in 0..4u32 {
|
||||
marker(&mut sample, 0xff02, 1, (p << 28) | 0x0800_0000);
|
||||
marker(&mut sample, 0xff03, 1, 4 << 19);
|
||||
}
|
||||
sample.extend([0x1e; 4]);
|
||||
sample
|
||||
}
|
||||
|
||||
pub(crate) fn lossless_file() -> Vec<u8> {
|
||||
file(&cmp1(4, 4, 4, 4, 0, 108), 4, 4, &lossless_sample())
|
||||
}
|
||||
|
||||
/// A 16×16 three-level C-RAW CRX stream of flat zero subbands (sensor level 8192).
|
||||
pub(crate) fn craw_sample() -> Vec<u8> {
|
||||
let mut sample = Vec::new();
|
||||
marker(&mut sample, 0xff01, 52, 0);
|
||||
for plane in 0..4u32 {
|
||||
marker(&mut sample, 0xff02, 13, (plane << 28) | 0x0800_0000);
|
||||
for band in 0..10u32 {
|
||||
marker(&mut sample, 0xff03, if band >= 7 { 2 } else { 1 }, (band << 28) | (4 << 19));
|
||||
}
|
||||
}
|
||||
for _ in 0..4 {
|
||||
sample.extend([0x80; 4]);
|
||||
sample.extend([0xf0; 3]);
|
||||
for _ in 0..3 {
|
||||
sample.extend([0xff, 0xf8]);
|
||||
}
|
||||
}
|
||||
sample
|
||||
}
|
||||
|
||||
pub(crate) fn craw_file() -> Vec<u8> {
|
||||
file(&cmp1(16, 16, 16, 16, 3, 540), 16, 16, &craw_sample())
|
||||
}
|
||||
}
|
||||
|
||||
/// Both fixtures decode as built, so the mutations below hit live decode paths.
|
||||
#[test]
|
||||
fn cr3_fixtures_decode() {
|
||||
let lossless = lightcraft_raw::decode(&cr3::lossless_file()).unwrap();
|
||||
assert_eq!((lossless.width, lossless.height, lossless.bits), (4, 4, 14));
|
||||
assert_eq!(lossless.data.get(0), 8193.0);
|
||||
let craw = lightcraft_raw::decode(&cr3::craw_file()).unwrap();
|
||||
assert_eq!((craw.width, craw.height, craw.bits), (16, 16, 14));
|
||||
assert_eq!(craw.data.get(0), 8192.0);
|
||||
assert_eq!(lightcraft_raw::probe_info(&cr3::craw_file()).unwrap(), craw.info());
|
||||
}
|
||||
|
||||
proptest! {
|
||||
/// Mutating or truncating structurally valid CR3 containers exercises the CRX marker walker and
|
||||
/// entropy readers far deeper than random TIFF bytes: errors or clean decodes, never a panic.
|
||||
#[test]
|
||||
fn mutated_cr3_containers_never_panic(
|
||||
kind in 0usize..2,
|
||||
flips in proptest::collection::vec((any::<usize>(), any::<u8>()), 1..24),
|
||||
cut in any::<usize>(),
|
||||
) {
|
||||
let mut data = if kind == 0 { cr3::lossless_file() } else { cr3::craw_file() };
|
||||
for (i, v) in flips {
|
||||
let n = data.len();
|
||||
data[i % n] = v;
|
||||
}
|
||||
let keep = if cut % 3 == 0 { cut % data.len() } else { data.len() };
|
||||
exercise(&data[..keep]);
|
||||
// The header-only description path (used when importing) must hold up against the same mutations.
|
||||
let _ = lightcraft_raw::probe_info(&data[..keep]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user