reader: a link to a file that is not there is not a dataset that exists

Every DECTRIS Eiger master links saturation_value, pixel_mask,
bit_depth_readout and serial_number into a companion <prefix>_meta.h5, and
that file is routinely not kept when a dataset is archived or deposited. The
existence test asked only whether the LINK was written, which it is, so every
optional-field guard in the reader answered yes and the read that followed
threw. A deposited Eiger 16M set could not be opened at all, over values the
reader was perfectly prepared to do without.

Exists() now asks the second question too - whether the object the link names
can be reached - so an orphaned link reads as absent and the fallbacks behind
it do their job.

The saturation value is then allowed to be missing outright, because on such a
file it is: neither the NXmx name nor the DECTRIS one is readable, and there is
no third place to look. Left unset, GetSaturationLimit() falls back to the
container's own overflow. That is the safe direction - it can only fail to call
a pixel saturated, where too LOW a value drops the whole reflection and
silently removes the strongest data - and the run says out loud that nothing
will be called saturated.

The set that could not be opened now processes to 2.17 A against a deposited
2.40 A, in the deposited space group, with a cell agreeing to 0.08%. Output is
byte-identical on datasets that already opened.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EFEJG6WBQv8th4UJFNe53N
(cherry picked from commit 0637979f6d95b446406ab70d1f3982841d195b36)
This commit is contained in:
2026-08-30 21:43:54 +02:00
parent fb18457e6f
commit 6ed4ea541e
4 changed files with 62 additions and 5 deletions
+31
View File
@@ -1451,3 +1451,34 @@ TEST_CASE("HDF5FilePusher_finalize_failure_recovers", "[HDF5FilePusher][Repro]")
std::filesystem::remove(e.path());
REQUIRE(H5Fget_obj_count(H5F_OBJ_ALL, H5F_OBJ_ALL) == 0);
}
// A link that is written in the file and points at something not there is NOT an existing dataset.
// Every DECTRIS Eiger master links saturation_value, pixel_mask, bit_depth_readout and
// serial_number into a companion <prefix>_meta.h5, and that file is routinely not kept when a
// dataset is archived or deposited. Asking only whether the LINK exists then answers yes and the
// read that follows throws, which turns every optional-field guard in the reader into a hard
// failure - measured on a deposited Eiger 16M set that could not be opened at all.
TEST_CASE("HDF5Objects_dangling_external_link_does_not_exist", "[HDF5][Unit]") {
const std::string fname = "test_dangling_link.h5";
remove(fname.c_str());
{
HDF5File file(fname);
HDF5Group group(file, "/entry");
group.SaveScalar("present", static_cast<int64_t>(7));
// ...and a link into a file that does not exist, exactly as an orphaned Eiger master has.
REQUIRE(H5Lcreate_external("no_such_meta.h5", "/_dectris/whatever",
group.GetID(), "absent", H5P_DEFAULT, H5P_DEFAULT) >= 0);
}
{
HDF5ReadOnlyFile file(fname);
CHECK(file.Exists("/entry/present"));
CHECK(file.GetInt("/entry/present") == 7);
// The link is there, the object is not.
CHECK(H5Lexists(file.GetID(), "/entry/absent", H5P_DEFAULT) > 0);
CHECK_FALSE(file.Exists("/entry/absent"));
// ...so an optional read of it falls back instead of throwing.
CHECK(file.GetString("/entry/absent", "fallback") == "fallback");
}
remove(fname.c_str());
REQUIRE(H5Fget_obj_count(H5F_OBJ_ALL, H5F_OBJ_ALL) == 0);
}