diff --git a/tests/HDF5WritingTest.cpp b/tests/HDF5WritingTest.cpp index 6576e78c..23be0d97 100644 --- a/tests/HDF5WritingTest.cpp +++ b/tests/HDF5WritingTest.cpp @@ -13,6 +13,64 @@ using namespace std::literals::chrono_literals; +TEST_CASE("HDF5Group_create_reopen_and_fail", "[HDF5][Unit]") { + { + HDF5File file("scratch_group_reopen.h5"); + + REQUIRE_NOTHROW(HDF5Group(file, "/group1")); + REQUIRE(file.Exists("/group1")); + + REQUIRE_NOTHROW(HDF5Group(file, "/group1")); + REQUIRE(file.Exists("/group1")); + + REQUIRE_THROWS(HDF5Group(file, "/missing_parent/group2")); + } + + remove("scratch_group_reopen.h5"); + REQUIRE(H5Fget_obj_count(H5F_OBJ_ALL, H5F_OBJ_ALL) == 0); +} + +TEST_CASE("HDF5Attr_string_update", "[HDF5][Unit]") { + const std::string first_value = "abc"; + const std::string second_value = "a much longer attribute value"; + + { + HDF5File file("scratch_attr_string_update.h5"); + REQUIRE_NOTHROW(file.Attr("str_attr", first_value)); + REQUIRE_NOTHROW(file.Attr("str_attr", second_value)); + } + + { + HDF5ReadOnlyFile file("scratch_attr_string_update.h5"); + REQUIRE(file.ReadAttrStr("str_attr") == second_value); + } + + remove("scratch_attr_string_update.h5"); + REQUIRE(H5Fget_obj_count(H5F_OBJ_ALL, H5F_OBJ_ALL) == 0); +} + +TEST_CASE("HDF5Attr_int64_update", "[HDF5][Unit]") { + const int64_t first_value = -1234567890123LL; + const int64_t second_value = 9876543210123LL; + + { + HDF5File file("scratch_attr_int64_update.h5"); + REQUIRE_NOTHROW(file.Attr("int_attr", first_value)); + REQUIRE(file.ReadAttrInt("int_attr") == first_value); + + REQUIRE_NOTHROW(file.Attr("int_attr", second_value)); + REQUIRE(file.ReadAttrInt("int_attr") == second_value); + } + + { + HDF5ReadOnlyFile file("scratch_attr_int64_update.h5"); + REQUIRE(file.ReadAttrInt("int_attr") == second_value); + } + + remove("scratch_attr_int64_update.h5"); + REQUIRE(H5Fget_obj_count(H5F_OBJ_ALL, H5F_OBJ_ALL) == 0); +} + TEST_CASE("HDF5DataSet_scalar", "[HDF5][Unit]") { uint16_t tmp_scalar = 16788; { diff --git a/writer/HDF5Objects.cpp b/writer/HDF5Objects.cpp index 0861a4db..7ed7a77d 100644 --- a/writer/HDF5Objects.cpp +++ b/writer/HDF5Objects.cpp @@ -315,76 +315,119 @@ void HDF5Fapl::SetVersionTo1p10orNewer() { H5Pset_libver_bounds(id, H5F_LIBVER_V110, H5F_LIBVER_LATEST); } +template +static HDF5Object& WriteOrCreateScalarAttr(HDF5Object& object, const std::string& name, const T& val) { + HDF5DataSpace dataspace; + HDF5DataType datatype(val); + + hid_t attr_id = -1; + + if (H5Aexists(object.GetID(), name.c_str()) > 0) { + attr_id = H5Aopen(object.GetID(), name.c_str(), H5P_DEFAULT); + if (attr_id < 0) + throw JFJochException(JFJochExceptionCategory::HDF5, "Cannot open attribute " + name); + + hid_t existing_type = H5Aget_type(attr_id); + if (existing_type < 0) { + H5Aclose(attr_id); + throw JFJochException(JFJochExceptionCategory::HDF5, "Cannot get attribute type " + name); + } + + const bool recreate = + (H5Tget_class(existing_type) != H5Tget_class(datatype.GetID())) || + (H5Tget_size(existing_type) != H5Tget_size(datatype.GetID())); + + H5Tclose(existing_type); + H5Aclose(attr_id); + + if (recreate) { + if (H5Adelete(object.GetID(), name.c_str()) < 0) + throw JFJochException(JFJochExceptionCategory::HDF5, "Cannot delete attribute " + name); + + attr_id = H5Acreate2(object.GetID(), name.c_str(), datatype.GetID(), dataspace.GetID(), H5P_DEFAULT, H5P_DEFAULT); + } else { + attr_id = H5Aopen(object.GetID(), name.c_str(), H5P_DEFAULT); + } + } else { + attr_id = H5Acreate2(object.GetID(), name.c_str(), datatype.GetID(), dataspace.GetID(), H5P_DEFAULT, H5P_DEFAULT); + } + + if (attr_id < 0) + throw JFJochException(JFJochExceptionCategory::HDF5, "Cannot create/open attribute " + name); + + herr_t ret = H5Awrite(attr_id, datatype.GetID(), &val); + H5Aclose(attr_id); + + if (ret < 0) + throw JFJochException(JFJochExceptionCategory::HDF5, "Attribute write unsuccessful"); + return object; +} + HDF5Object & HDF5Object::Attr(const std::string &name, const std::string &val) { HDF5DataSpace dataspace; HDF5DataType datatype(val); - hid_t attr_id = H5Acreate2(id, name.c_str(), datatype.GetID(), dataspace.GetID(), H5P_DEFAULT, H5P_DEFAULT); + hid_t attr_id = -1; + + if (H5Aexists(id, name.c_str()) > 0) { + attr_id = H5Aopen(id, name.c_str(), H5P_DEFAULT); + if (attr_id < 0) + throw JFJochException(JFJochExceptionCategory::HDF5, "Cannot open attribute " + name); + + hid_t existing_type = H5Aget_type(attr_id); + if (existing_type < 0) { + H5Aclose(attr_id); + throw JFJochException(JFJochExceptionCategory::HDF5, "Cannot get attribute type " + name); + } + + const bool recreate = + (H5Tget_class(existing_type) != H5T_STRING) || + (H5Tget_size(existing_type) < val.length() + 1); + + H5Tclose(existing_type); + H5Aclose(attr_id); + + if (recreate) { + if (H5Adelete(id, name.c_str()) < 0) + throw JFJochException(JFJochExceptionCategory::HDF5, "Cannot delete attribute " + name); + + attr_id = H5Acreate2(id, name.c_str(), datatype.GetID(), dataspace.GetID(), H5P_DEFAULT, H5P_DEFAULT); + } else { + attr_id = H5Aopen(id, name.c_str(), H5P_DEFAULT); + } + } else { + attr_id = H5Acreate2(id, name.c_str(), datatype.GetID(), dataspace.GetID(), H5P_DEFAULT, H5P_DEFAULT); + } + + if (attr_id < 0) + throw JFJochException(JFJochExceptionCategory::HDF5, "Cannot create/open attribute " + name); + herr_t ret = H5Awrite(attr_id, datatype.GetID(), val.c_str()); H5Aclose(attr_id); - if (ret < 0) throw JFJochException(JFJochExceptionCategory::HDF5, "Attribute write unsuccessful"); + if (ret < 0) + throw JFJochException(JFJochExceptionCategory::HDF5, "Attribute write unsuccessful"); return *this; } HDF5Object & HDF5Object::Attr(const std::string &name, int32_t val) { - HDF5DataSpace dataspace; - HDF5DataType datatype(val); - - hid_t attr_id = H5Acreate2(id, name.c_str(), datatype.GetID(), dataspace.GetID(), H5P_DEFAULT, H5P_DEFAULT); - herr_t ret = H5Awrite(attr_id, datatype.GetID(), &val); - H5Aclose(attr_id); - - if (ret < 0) throw JFJochException(JFJochExceptionCategory::HDF5, "Atrribute write unsucessful"); - return *this; + return WriteOrCreateScalarAttr(*this, name, val); } HDF5Object & HDF5Object::Attr(const std::string &name, uint32_t val) { - HDF5DataSpace dataspace; - HDF5DataType datatype(val); - - hid_t attr_id = H5Acreate2(id, name.c_str(), datatype.GetID(), dataspace.GetID(), H5P_DEFAULT, H5P_DEFAULT); - herr_t ret = H5Awrite(attr_id, datatype.GetID(), &val); - H5Aclose(attr_id); - - if (ret < 0) throw JFJochException(JFJochExceptionCategory::HDF5, "Atrribute write unsucessful"); - return *this; + return WriteOrCreateScalarAttr(*this, name, val); } HDF5Object & HDF5Object::Attr(const std::string &name, int64_t val) { - HDF5DataSpace dataspace; - HDF5DataType datatype(val); - - hid_t attr_id = H5Acreate2(id, name.c_str(), datatype.GetID(), dataspace.GetID(), H5P_DEFAULT, H5P_DEFAULT); - herr_t ret = H5Awrite(attr_id, datatype.GetID(), &val); - H5Aclose(attr_id); - - if (ret < 0) throw JFJochException(JFJochExceptionCategory::HDF5, "Atrribute write unsucessful"); - return *this; + return WriteOrCreateScalarAttr(*this, name, val); } HDF5Object & HDF5Object::Attr(const std::string &name, uint64_t val) { - HDF5DataSpace dataspace; - HDF5DataType datatype(val); - - hid_t attr_id = H5Acreate2(id, name.c_str(), datatype.GetID(), dataspace.GetID(), H5P_DEFAULT, H5P_DEFAULT); - herr_t ret = H5Awrite(attr_id, datatype.GetID(), &val); - H5Aclose(attr_id); - - if (ret < 0) throw JFJochException(JFJochExceptionCategory::HDF5, "Atrribute write unsucessful"); - return *this; + return WriteOrCreateScalarAttr(*this, name, val); } HDF5Object & HDF5Object::Attr(const std::string &name, double val) { - HDF5DataSpace dataspace; - HDF5DataType datatype(val); - - hid_t attr_id = H5Acreate2(id, name.c_str(), datatype.GetID(), dataspace.GetID(), H5P_DEFAULT, H5P_DEFAULT); - herr_t ret = H5Awrite(attr_id, datatype.GetID(), &val); - H5Aclose(attr_id); - - if (ret < 0) throw JFJochException(JFJochExceptionCategory::HDF5, "Atrribute write unsucessful"); - return *this; + return WriteOrCreateScalarAttr(*this, name, val); } HDF5Object & HDF5Object::Attr(const std::string &name, const std::vector &val) { @@ -632,14 +675,19 @@ HDF5Group::HDF5Group(const HDF5Object& parent, const std::string &name) : HDF5Gr } HDF5Group::HDF5Group(const HDF5Object& parent, const char *name) : HDF5Object() { - id = H5Gcreate(parent.GetID(), name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (H5Lexists(parent.GetID(), name, H5P_DEFAULT) > 0) + id = H5Gopen(parent.GetID(), name, H5P_DEFAULT); + else + id = H5Gcreate(parent.GetID(), name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + + if (id < 0) + throw JFJochException(JFJochExceptionCategory::HDF5, "Cannot open/create HDF5 group " + std::string(name)); } HDF5Group::~HDF5Group() { H5Gclose(id); } - HDF5File::HDF5File(const std::string& filename, bool v1_10) : HDF5Object() { HDF5Fapl fapl;