Skip to content

Commit 0863769

Browse files
fix(fiff): fall back to header walk when DIR_POINTER points past EOF [#K1]
Observed on ds002885 (169 MB FIFF): DIR_POINTER payload = 911,773,376 (0x36588EC0), well past the 168,755,200-byte EOF. parseDirectory threw "dir at abs 911773376 not inside view" and the whole open() failed. MNE-Python's _fiff/open.py:183-192 treats out-of-range DIR_POINTER the same as the -1 sentinel: fall back to sequential header walk. We now match that behaviour, gated on `dirOffset < 0 || dirOffset >= totalBytes`. Also wrap the in-range parseDirectory call in try/catch so any malformed-tag error at the claimed offset triggers the walk fallback instead of crashing. After the fix, ds002885 walks the head and correctly identifies the file as a calibration-only FIFF (no FIFFB_RAW_DATA block), joining ds003392 and ds003352 in the documented intentional-rejection bucket.
1 parent 2ea3e5c commit 0863769

1 file changed

Lines changed: 23 additions & 11 deletions

File tree

formats/fiff.js

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -422,23 +422,35 @@
422422

423423
const dirInfo = globalThis.FiffDir.readDirPointer(headView);
424424

425-
// dir.entries: array of {kind,type,size,position}. Two paths to
426-
// build it:
427-
// (a) DIR_POINTER is set → parse the end-of-file FIFF_DIR tag
428-
// (fast: 1 range fetch of the tail).
429-
// (b) DIR_POINTER payload is -1 (or DIR_POINTER tag is missing)
430-
// → walk the tag header stream sequentially. Big buffers
431-
// are skipped via pos += 16 + size, so total fetches scale
432-
// with N_TAGS, not file size. See MNE-Python
433-
// mne/_fiff/open.py:183-192 for the canonical reference.
425+
// dir.entries: array of {kind,type,size,position}. Three paths:
426+
// (a) DIR_POINTER is set + dirOffset is in-range → parse the
427+
// end-of-file FIFF_DIR tag (fast: 1 range fetch of the tail).
428+
// (b) DIR_POINTER payload is -1 (sentinel for "no directory") →
429+
// walk the tag header stream sequentially.
430+
// (c) DIR_POINTER payload is a positive value BUT points past
431+
// end-of-file (malformed/truncated/split file — observed on
432+
// ds002885 where DIR_POINTER = 911M but file is 169M). MNE-
433+
// Python `_fiff/open.py:183-192` treats this as case (b) and
434+
// falls back to sequential walk; we match that behaviour.
434435
let dir;
435-
if (!dirInfo.hasDirectory) {
436+
const dirOutOfRange = dirInfo.hasDirectory &&
437+
(dirInfo.dirOffset < 0 || dirInfo.dirOffset >= totalBytes);
438+
if (!dirInfo.hasDirectory || dirOutOfRange) {
436439
const fetchRange = async (s, e) =>
437440
globalThis.HttpRange.rangeFetch(url, s, e, e - s + 1);
438441
dir = await globalThis.FiffDir.buildDirectoryByHeaderWalk(url, totalBytes, fetchRange);
439442
} else {
440443
// Parse directory entries → block ranges.
441-
dir = globalThis.FiffDir.parseDirectory(shiftedView, dirInfo.dirOffset);
444+
try {
445+
dir = globalThis.FiffDir.parseDirectory(shiftedView, dirInfo.dirOffset);
446+
} catch (e) {
447+
// Defensive: even if dirOffset is in-range, the FIFF_DIR tag
448+
// at that offset can be malformed. Same behaviour as MNE on
449+
// any directory-parse error — fall back to header walk.
450+
const fetchRange = async (s, e2) =>
451+
globalThis.HttpRange.rangeFetch(url, s, e2, e2 - s + 1);
452+
dir = await globalThis.FiffDir.buildDirectoryByHeaderWalk(url, totalBytes, fetchRange);
453+
}
442454
}
443455
// To identify block IDs we need the 4-byte payload at each
444456
// BLOCK_START tag's position+16. For huge files those positions

0 commit comments

Comments
 (0)