HDF5 reader: read variable-length string attributes and datasets
ReadAttrStr and HDF5DataSet::ReadString read every string as fixed-length, sizing the buffer by H5Tget_size. For a variable-length string (what h5py writes for a str, UTF-8) that size is a pointer, so the read put a char* into the buffer and returned garbage. transformation_type then never equalled "rotation", and an EIGER master whose goniometer attributes were written that way (depends_on, transformation_type, units on /entry/sample/transformations) was processed as stills although it holds a valid rotation sweep. All string reads now go through one helper, ReadHDF5String, which handles both fixed- and variable-length strings in either character set; ReadElement<string> already handled the variable-length case and now uses the same helper. Test: JFJochReader_Goniometer_VariableLengthStrings reads the rotation axis from a master with fixed-length ASCII and with variable-length UTF-8 string attributes and depends_on dataset. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SVmAWnzCmRKAXVUCdc4iNi
This commit is contained in:
1 parent
0457cecafa
commit
badd7360ca
3 files changed
+136
-47
No files matched your search
+37
-22
@@ -506,6 +506,31 @@ HDF5Object & HDF5Object::Attr(const std::string &name, const std::vector<double>
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string ReadHDF5String(hid_t type_id, const std::function<herr_t(hid_t, void *)> &read) {
|
||||
if (H5Tis_variable_str(type_id) > 0) {
|
||||
char *tmp = nullptr;
|
||||
if (read(type_id, &tmp) < 0)
|
||||
throw JFJochException(JFJochExceptionCategory::HDF5, "Cannot read variable-length string");
|
||||
std::string out = (tmp != nullptr) ? std::string(tmp) : std::string{};
|
||||
if (tmp != nullptr)
|
||||
H5free_memory(tmp);
|
||||
return out;
|
||||
}
|
||||
|
||||
const size_t size = H5Tget_size(type_id);
|
||||
if (size == 0)
|
||||
return "";
|
||||
|
||||
std::string buffer(size, '\0');
|
||||
if (read(type_id, buffer.data()) < 0)
|
||||
throw JFJochException(JFJochExceptionCategory::HDF5, "Cannot read string");
|
||||
|
||||
const size_t end = buffer.find('\0');
|
||||
if (end != std::string::npos)
|
||||
buffer.resize(end);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::vector<double> HDF5Object::ReadAttrVec(const std::string &name) {
|
||||
hid_t attr_id = H5Aopen(id, name.c_str(), H5P_DEFAULT);
|
||||
if (attr_id < 0)
|
||||
@@ -579,11 +604,10 @@ std::string HDF5Object::ReadAttrStr(const std::string &name) {
|
||||
throw JFJochException(JFJochExceptionCategory::HDF5, "Attribute " + name + " is not a string");
|
||||
}
|
||||
|
||||
// Get string size and read it
|
||||
size_t size = H5Tget_size(type_id);
|
||||
std::vector<char> buffer(size + 1, '\0'); // +1 for null terminator to be safe
|
||||
|
||||
if (H5Aread(attr_id, type_id, buffer.data()) < 0) {
|
||||
std::string ret;
|
||||
try {
|
||||
ret = ReadHDF5String(type_id, [&](hid_t type, void *buffer) { return H5Aread(attr_id, type, buffer); });
|
||||
} catch (...) {
|
||||
H5Tclose(type_id);
|
||||
H5Aclose(attr_id);
|
||||
throw JFJochException(JFJochExceptionCategory::HDF5, "Cannot read attribute " + name);
|
||||
@@ -591,7 +615,7 @@ std::string HDF5Object::ReadAttrStr(const std::string &name) {
|
||||
|
||||
H5Tclose(type_id);
|
||||
H5Aclose(attr_id);
|
||||
return {buffer.data()};
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -923,22 +947,13 @@ std::string HDF5DataSet::ReadString() const {
|
||||
}
|
||||
|
||||
HDF5DataType file_data_type(*this);
|
||||
const size_t size = file_data_type.GetElemSize();
|
||||
|
||||
if (size == 0)
|
||||
return "";
|
||||
|
||||
std::string buffer(size, '\0');
|
||||
|
||||
if (H5Dread(id, file_data_type.GetID(), H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer.data()) < 0)
|
||||
throw JFJochException(JFJochExceptionCategory::HDF5,
|
||||
dataset_name + ": read unsuccessful");
|
||||
|
||||
const size_t end = buffer.find('\0');
|
||||
if (end != std::string::npos)
|
||||
buffer.resize(end);
|
||||
|
||||
return buffer;
|
||||
try {
|
||||
return ReadHDF5String(file_data_type.GetID(), [&](hid_t type, void *buffer) {
|
||||
return H5Dread(id, type, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer);
|
||||
});
|
||||
} catch (const JFJochException &) {
|
||||
throw JFJochException(JFJochExceptionCategory::HDF5, dataset_name + ": read unsuccessful");
|
||||
}
|
||||
}
|
||||
|
||||
void HDF5DataSet::Close() {
|
||||
|
||||
Reference in new issue
Block a user