Mantid
Loading...
Searching...
No Matches
NexusHDF5Descriptor.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2007 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
7
9
10#include <boost/multi_index/detail/index_matcher.hpp>
11
13
14#include <hdf5.h>
15
16#include <cstdlib> // malloc, calloc
17#include <cstring> // strcpy
18#include <stdexcept> // std::invalid_argument
19#include <utility>
20
21using boost::multi_index::detail::index_matcher::entry;
22
23namespace Mantid::Kernel {
24
27namespace {
28
35herr_t readStringAttribute(hid_t attr, char **data) {
36 herr_t iRet = 0;
37 hsize_t thedims[H5S_MAX_RANK];
38
39 hid_t atype = H5Aget_type(attr);
40 hsize_t sdim = H5Tget_size(atype);
41 hid_t space = H5Aget_space(attr);
42 int ndims = H5Sget_simple_extent_dims(space, thedims, NULL);
43
44 if (ndims == 0) {
45 if (H5Tis_variable_str(atype)) {
46 hid_t btype = H5Tget_native_type(atype, H5T_DIR_ASCEND);
47 iRet = H5Aread(attr, btype, data);
48 H5Tclose(btype);
49 } else {
50 *data = (char *)malloc(sdim + 1);
51 iRet = H5Aread(attr, atype, *data);
52 (*data)[sdim] = '\0';
53 }
54 } else if (ndims == 1) {
55 unsigned int i;
56 char **strings;
57
58 strings = (char **)malloc(thedims[0] * sizeof(char *));
59
60 if (!H5Tis_variable_str(atype)) {
61 strings[0] = (char *)malloc(thedims[0] * sdim * sizeof(char));
62 for (i = 1; i < thedims[0]; i++) {
63 strings[i] = strings[0] + i * sdim;
64 }
65 }
66
67 iRet = H5Aread(attr, atype, strings[0]);
68 *data = (char *)calloc((sdim + 2) * thedims[0], sizeof(char));
69 for (i = 0; i < thedims[0]; i++) {
70 if (i == 0) {
71 strncpy(*data, strings[i], sdim);
72 } else {
73 strcat(*data, ", ");
74 strncat(*data, strings[i], sdim);
75 }
76 }
77 if (H5Tis_variable_str(atype)) {
78 H5Dvlen_reclaim(atype, space, H5P_DEFAULT, strings);
79 } else {
80 free(strings[0]);
81 }
82
83 free(strings);
84 } else {
85 *data = (char *)malloc(33);
86 strcpy(*data, " higher dimensional string array\0");
87 }
88
89 H5Tclose(atype);
90 H5Sclose(space);
91 if (iRet < 0)
92 return -1;
93 return 0;
94}
95
101std::pair<std::string, herr_t> readStringAttributeN(hid_t attr) {
102 char *vdat = NULL;
103 const auto iRet = readStringAttribute(attr, &vdat);
104 std::string attrData;
105 if (iRet >= 0) {
106 attrData = vdat;
107 }
108 return std::make_pair(attrData, iRet);
109}
110
111void getGroup(hid_t groupID, std::map<std::string, std::set<std::string>> &allEntries) {
112
116 auto lf_getNxClassAttribute = [&](hid_t groupID, const char *objectName) -> std::string {
117 std::string attribute = "";
118 hid_t attributeID = H5Aopen_by_name(groupID, objectName, "NX_class", H5P_DEFAULT, H5P_DEFAULT);
119 if (attributeID < 0) {
120 H5Aclose(attributeID);
121 return attribute;
122 }
123
124 auto pairData = readStringAttributeN(attributeID);
125 // already null terminated in readStringAttributeN
126 attribute = pairData.first;
127 H5Aclose(attributeID);
128
129 return attribute;
130 };
131
132 // using HDF5 C API
133 constexpr std::size_t maxLength = 1024;
134 char groupName[maxLength];
135 char memberName[maxLength];
136 std::size_t groupNameLength = static_cast<std::size_t>(H5Iget_name(groupID, groupName, maxLength));
137 hsize_t nObjects = 0;
138 H5Gget_num_objs(groupID, &nObjects);
139
140 const std::string groupNameStr(groupName, groupNameLength);
141 const std::string nxClass = (groupNameStr == "/") ? "" : lf_getNxClassAttribute(groupID, groupNameStr.c_str());
142
143 if (!nxClass.empty()) {
144 allEntries[nxClass].insert(groupNameStr);
145 }
146
147 for (unsigned int i = 0; i < nObjects; ++i) {
148
149 const int type = H5Gget_objtype_by_idx(groupID, static_cast<size_t>(i));
150 const std::size_t memberNameLength =
151 static_cast<std::size_t>(H5Gget_objname_by_idx(groupID, static_cast<hsize_t>(i), memberName, maxLength));
152
153 if (type == H5O_TYPE_GROUP) {
154 hid_t subGroupID = H5Gopen2(groupID, memberName, H5P_DEFAULT);
155 getGroup(subGroupID, allEntries);
156 H5Gclose(subGroupID);
157
158 } else if (type == H5O_TYPE_DATASET) {
159 const std::string memberNameStr(memberName, memberNameLength);
160 const std::string absoluteEntryName = groupNameStr + "/" + memberNameStr;
161 allEntries["SDS"].insert(absoluteEntryName);
162 }
163 }
164}
165
166} // namespace
167
168bool NexusHDF5Descriptor::isReadable(const std::string &filename) {
169 // use existing function to do the work
171}
172
174 : m_filename(std::move(filename)), m_allEntries(initAllEntries()) {}
175
176// PUBLIC
177std::string NexusHDF5Descriptor::getFilename() const noexcept { return m_filename; }
178
179const std::map<std::string, std::set<std::string>> &NexusHDF5Descriptor::getAllEntries() const noexcept {
180 return m_allEntries;
181}
182
183// PRIVATE
184std::map<std::string, std::set<std::string>> NexusHDF5Descriptor::initAllEntries() {
185
186 hid_t fapl = H5Pcreate(H5P_FILE_ACCESS);
187 H5Pset_fclose_degree(fapl, H5F_CLOSE_STRONG);
188
189 hid_t fileID = H5Fopen(m_filename.c_str(), H5F_ACC_RDONLY, fapl);
190 if (fileID < 0) {
191
192 throw std::invalid_argument("ERROR: Kernel::NexusHDF5Descriptor couldn't open hdf5 file " + m_filename +
193 " with fapl " + std::to_string(fapl) + "\n");
194 }
195
196 hid_t groupID = H5Gopen2(fileID, "/", H5P_DEFAULT);
197
198 std::map<std::string, std::set<std::string>> allEntries;
199 // scan file recursively starting with root group "/"
200 getGroup(groupID, allEntries);
201 H5Gclose(groupID);
202 H5Fclose(fileID);
203
204 // rely on move semantics
205 return allEntries;
206}
207
208bool NexusHDF5Descriptor::isEntry(const std::string &entryName, const std::string &groupClass) const noexcept {
209
210 auto itClass = m_allEntries.find(groupClass);
211 if (itClass == m_allEntries.end()) {
212 return false;
213 }
214
215 if (itClass->second.count(entryName) == 1) {
216 return true;
217 }
218
219 return false;
220}
221
222bool NexusHDF5Descriptor::isEntry(const std::string &entryName) const noexcept {
223
224 for (auto itClass = m_allEntries.rbegin(); itClass != m_allEntries.rend(); ++itClass) {
225 if (itClass->second.count(entryName) == 1) {
226 return true;
227 }
228 }
229 return false;
230}
231
232} // namespace Mantid::Kernel
static bool isReadable(const std::string &filename, const Version version=AnyVersion)
Returns true if the file is considered to store data in a hierarchy.
static bool isReadable(const std::string &filename)
Returns true if the file is considered to store data in a hierarchy.
const std::map< std::string, std::set< std::string > > m_allEntries
All entries metadata.
const std::map< std::string, std::set< std::string > > & getAllEntries() const noexcept
Returns a const reference of the internal map holding all entries in the NeXus HDF5 file.
std::string getFilename() const noexcept
Returns a copy of the current file name.
bool isEntry(const std::string &entryName, const std::string &groupClass) const noexcept
Checks if a full-path entry exists for a particular groupClass in a Nexus dataset.
std::string m_filename
NeXus HDF5 file name.
std::map< std::string, std::set< std::string > > initAllEntries()
Sets m_allEntries, called in HDF5 constructor.
STL namespace.
std::string to_string(const wide_integer< Bits, Signed > &n)