Skip to content

feat(formats)!: migrate ARW/CR2/NEF/CR3 IFD walking to gamut-ifd - #45

Merged
justin13888 merged 1 commit into
masterfrom
20-migrate-tiff-parser-gamut-ifd
Jul 19, 2026
Merged

feat(formats)!: migrate ARW/CR2/NEF/CR3 IFD walking to gamut-ifd#45
justin13888 merged 1 commit into
masterfrom
20-migrate-tiff-parser-gamut-ifd

Conversation

@justin13888

Copy link
Copy Markdown
Collaborator

Addresses the unblocked scope of #20; the binrw module deletion completes in #21 (see sequencing note), and the CR3 ISOBMFF slice stays blocked on gamut#301.

Stacked on #44 (base 19-migrate-metadata-gamut); merge order #42#44 → this.

What is Delivered

  • ARW / CR2 / NEF IFD walking on gamut-ifd — whole-file slice parsing via gamut_ifd::read with explicit pointer-tag resolution, replacing the binrw TiffParser in those decoders. ARW keeps exact SR2 semantics: the encrypted sub-IFD reads lazily via IfdReader (a garbled entry fails only its own value fetch); MakerNote probing becomes bounds-checked slice reads.
  • CR3's CMT1–CMT4 embedded TIFF blocks parse via a lazy IfdReader. The hand-rolled ISOBMFF box walker is deliberately untouched — blocked upstream (gamut#301: BoxReader rejects 64-bit largesize, which real CR3 mdat boxes use).
  • New formats/ifd.rs (feature ifd-parser = dep:gamut-ifd + bigtiff): the private vendor tag catalogue as data over the gamut engine per the issue — baseline TIFF/EXIF/GPS/DNG-block tags + the Sony SR2 set — plus a lenient tree resolver matching the old warn-and-skip on malformed sub-IFDs, value coercions, bounds-checked reads, and ports of the metadata_helper extractors.
  • Feature graph: arw/cr2/cr3/nef-decode no longer pull tiff-parser/binrw; dng-decode pulls both (its detection sniff migrated to a streaming reader; full DNG migration is Migrate DNG decode/encode to gamut-dng #21); codecs/{ljpeg,bit_pump} re-gated onto their actual consumers.
  • RAF audited (issue requirement): zero tiff-parser usage confirmed; untouched.
  • Tests ported: new tests/ifd_decoder_tests.rs re-expresses the decoder-level behaviours of tiff_parser_tests.rs on the new path against the real Sony fixtures; parser-internal tests stay with the binrw module and die with it in Migrate DNG decode/encode to gamut-dng #21.

Deliberate sequencing deviation

The issue text deletes src/tiff/ here. dng.rs is that module's last consumer, and #21 replaces dng.rs wholesale with gamut-dng (which does its own IFD walking) — migrating dng.rs onto gamut-ifd now would mean rewriting it twice in consecutive PRs. The binrw module stays compiled behind tiff-parser for exactly one more PR; #21 deletes it with the binrw dep, the tiff-parser feature, and the public TiffParser/TiffWriter/TiffValue API (the 0.x break the issue accepts).

Behaviour notes (fixture-verified neutral)

Eager value decoding (corrupt top-level offsets fail at parse, not first touch; sub-IFD children stay lenient); ASCII normalization kept byte-identical via rawshift-side clean_ascii; CFAPattern now also read from UNDEFINED-typed fields (was BYTE-only with silent RGGB fallback — a small correctness win); OOB strip reads surface as ParseError::OffsetOutOfBounds; new ungated ParseError::MissingTag(u16) with numeric display.

Verification Done

Run independently after implementation:

  • cargo test --workspace383 passed, 0 failed
  • cargo test -p rawshift-image --features full576 passed, 0 failed (fixtures fetched; ARW suite ran against the real 40 MB Sony files — raw-frame discovery, dimensions, bit depth, Make/Model, strip bounds asserted on the new path; CR2/CR3/NEF fixture tests skip as before, no upstream fixtures exist)
  • cargo clippy -p rawshift-image --all-targets --features full -- -D warnings — clean; workspace clippy — clean
  • Standalone builds --no-default-features --features {arw,cr2,cr3,nef,dng} — zero errors each
  • lefthook pre-commit — green

Rebuilds the Sony ARW, Canon CR2, Nikon NEF, and Canon CR3 (CMT1-CMT4 TIFF
block) decoders on gamut_ifd, replacing their use of the in-repo binrw TIFF
parser. New shared module formats/ifd.rs (feature `ifd-parser`) carries the
private tag catalogue as data over the gamut engine — baseline TIFF/EXIF/GPS/
DNG-block tags plus the Sony SR2 set — together with a lenient tree resolver
(malformed sub-IFD warns and skips, matching the old parser), value
coercions, bounds-checked range reads, and gamut-based ports of the
metadata_helper extractors.

ARW keeps its SR2 semantics: the encrypted sub-IFD is read lazily via
IfdReader so a garbled entry fails only its own value fetch, and the
MakerNote absolute-offset probe becomes a bounds-checked slice read. CR3's
ISOBMFF box walker is untouched — that slice is blocked upstream
(visualcommons/gamut#301, 64-bit largesize + uuid usertype); only its embedded
TIFF blocks move. RAF audited: confirmed free of tiff-parser usage, no
change. DNG and the TIFF writer are deliberately NOT migrated here: gamut-dng
subsumes DNG's IFD walking wholesale in #21, which is when src/tiff/ loses
its last consumer and is deleted along with binrw — migrating dng.rs twice
serves nobody.

Feature graph: arw/cr2/cr3/nef-decode now pull `ifd-parser` (dep:gamut-ifd,
bigtiff enabled) instead of `tiff-parser`; dng-decode pulls both (format
detection's Make/DNGVersion sniff migrated to a streaming IfdReader);
dng-encode keeps tiff-parser alone. codecs/{ljpeg,bit_pump} re-gated onto the
decoders that actually use them instead of the parser feature.

Error surface: new ungated ParseError::MissingTag(u16) (numeric replacement
for the TiffTag-typed TagNotFound, which stays gated until #21).

Behaviour notes (fixture-neutral, verified against real Sony ARW files):
- gamut decodes IFD values eagerly at parse; files with corrupt top-level
  value offsets now fail at parse instead of on first touch. Sub-IFD children
  stay lenient.
- ASCII handling normalized rawshift-side (clean_ascii) so metadata strings
  are byte-identical to before despite gamut preserving interior NULs.
- CFAPattern now also accepted from UNDEFINED-typed fields (previously BYTE
  only, silently defaulting to RGGB).
- Out-of-bounds strip/thumbnail reads surface as ParseError::OffsetOutOfBounds
  instead of io UnexpectedEof.

Tests: new tests/ifd_decoder_tests.rs ports the decoder-level assertions of
tiff_parser_tests.rs to the new path against real fixtures; parser-internal
tests remain with the binrw module and are deleted with it in #21.

BREAKING CHANGE: MissingTag display uses numeric tag ids; ARW/CR2/CR3/NEF
builds no longer enable the `tiff-parser` feature.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant