From c77ebe56c3e092a06bd5434344016c52aa887c0d Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Thu, 3 Sep 2026 10:58:27 +0200 Subject: [PATCH] reader: a one-element string array is a scalar Masters exist that store sensor_material, description and the compression name as shape (1,) rather than as true scalars - JUNGFRAU files from an early beamline deployment do, and the application definition permits it. ReadString() required rank 0 and threw on anything else, so the whole file was lost: the metadata parse never finished and no image was ever examined. Accept any shape holding exactly one element, and keep throwing for a string dataset that genuinely holds several, which is a different quantity and cannot be read into one std::string. The numeric path already worked this way (HDF5DataSet_scalar_stored_rank1); this is the string half. Measured: three long-wavelength rotation datasets that could not be opened at all now process end to end. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01EFEJG6WBQv8th4UJFNe53N --- tests/HDF5WritingTest.cpp | 20 ++++++++++++++++++++ writer/HDF5Objects.cpp | 15 +++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/tests/HDF5WritingTest.cpp b/tests/HDF5WritingTest.cpp index 822c64032..bc18fc1a3 100644 --- a/tests/HDF5WritingTest.cpp +++ b/tests/HDF5WritingTest.cpp @@ -137,6 +137,26 @@ TEST_CASE("HDF5DataSet_string", "[HDF5][Unit]") { REQUIRE (H5Fget_obj_count(H5F_OBJ_ALL, H5F_OBJ_ALL) == 0); } +TEST_CASE("HDF5DataSet_string_stored_rank1", "[HDF5][Unit]") { + // Facilities write a one-element string array where the application definition allows a scalar - + // JUNGFRAU masters from an early beamline deployment store sensor_material and description that + // way - and the file is unreadable if that is refused, because the metadata parse never finishes. + { + HDF5File file("scratch2b.h5"); + file.SaveVector("one", std::vector{"Si"}); + file.SaveVector("two", std::vector{"Si", "CdTe"}); + } + { + HDF5ReadOnlyFile file("scratch2b.h5"); + HDF5DataSet one(file, "one"); + CHECK(HDF5DataSpace(one).GetNumOfDimensions() == 1); + CHECK(one.ReadString() == "Si"); + REQUIRE_THROWS(HDF5DataSet(file, "two").ReadString()); + } + remove("scratch2b.h5"); + REQUIRE (H5Fget_obj_count(H5F_OBJ_ALL, H5F_OBJ_ALL) == 0); +} + TEST_CASE("HDF5DataSet_vector", "[HDF5][Unit]") { std::vector tmp_vector (16384); tmp_vector[0] = 599.88; diff --git a/writer/HDF5Objects.cpp b/writer/HDF5Objects.cpp index 5f3ef778f..dd7e2393e 100644 --- a/writer/HDF5Objects.cpp +++ b/writer/HDF5Objects.cpp @@ -908,8 +908,19 @@ HDF5DataSet& HDF5DataSet::WriteDirectChunk(const void *data, hsize_t data_size, std::string HDF5DataSet::ReadString() const { HDF5DataSpace file_space(*this); - if (file_space.GetNumOfDimensions() != 0) - throw JFJochException(JFJochExceptionCategory::HDF5, "Dataset tries to read string (scalar) from vector dataset"); + // A rank-1 dataset holding exactly one string is the same thing as a scalar, and facilities + // write it both ways - JUNGFRAU masters from an early beamline deployment store + // sensor_material and description as shape (1,). Refusing those loses the whole file over a + // difference that carries no information. + if (file_space.GetNumOfDimensions() != 0) { + const auto dims = file_space.GetDimensions(); + hsize_t elements = 1; + for (const auto d : dims) + elements *= d; + if (elements != 1) + throw JFJochException(JFJochExceptionCategory::HDF5, + "Dataset tries to read string (scalar) from vector dataset"); + } HDF5DataType file_data_type(*this); const size_t size = file_data_type.GetElemSize();