andres::graph
hdf5.hxx
1 #pragma once
2 #ifndef ANDRES_GRAPH_HDF5_HXX
3 #define ANDRES_GRAPH_HDF5_HXX
4 
5 #include <cassert>
6 #include <string>
7 #include <vector>
8 #include <array>
9 #include <sstream>
10 
11 // compat fix for buggy hdf5 1.8 versions
12 #include <H5version.h>
13 #if (H5_VERS_MAJOR == 1 && H5_VERS_MINOR >= 8 && defined(H5_USE_16_API_DEFAULT))
14 #define H5Gcreate_vers 2
15 #define H5Gopen_vers 2
16 #define H5Dopen_vers 2
17 #define H5Dcreate_vers 2
18 #define H5Acreate_vers 2
19 #endif
20 
21 #include "hdf5.h"
22 
23 #ifndef NDEBUG
24 #define ANDRES_GRAPH_HDF5_DEBUG true
25 #else
26 #define ANDRES_GRAPH_HDF5_DEBUG false
27 #endif
28 
29 namespace andres{
30 namespace graph{
31 namespace hdf5{
32 
33 template<class GRAPH>
34 class GraphTraitsHDF5;
35 
36 // \cond suppress doxygen
37 template<bool B = true> class HandleCheck;
38 template<> class HandleCheck<true> {
39 public:
40  HandleCheck()
41  { counter_ = H5Fget_obj_count(H5F_OBJ_ALL, H5F_OBJ_ALL); }
42  ~HandleCheck()
43  { assert( counter_ == H5Fget_obj_count(H5F_OBJ_ALL, H5F_OBJ_ALL)); }
44 private:
45  ssize_t counter_;
46 };
47 
48 template<> class HandleCheck<false> {
49 public:
50  void check() {}
51 };
52 // \endcond
53 
54 template<class T>
55 inline hid_t uintTypeHelper() {
56  switch(sizeof(T)) {
57  case 1: return H5T_STD_U8LE;
58  case 2: return H5T_STD_U16LE;
59  case 4: return H5T_STD_U32LE;
60  case 8: return H5T_STD_U64LE;
61  default: throw std::runtime_error("No matching HDF5 type.");
62  }
63 }
64 
65 template<class T>
66 inline hid_t intTypeHelper() {
67  switch(sizeof(T)) {
68  case 1: return H5T_STD_I8LE;
69  case 2: return H5T_STD_I16LE;
70  case 4: return H5T_STD_I32LE;
71  case 8: return H5T_STD_I64LE;
72  default: throw std::runtime_error("No matching HDF5 type.");
73  }
74 }
75 
76 template<class T>
77 inline hid_t floatingTypeHelper() {
78  switch(sizeof(T)) {
79  case 4: return H5T_IEEE_F32LE;
80  case 8: return H5T_IEEE_F64LE;
81  default: throw std::runtime_error("No matching HDF5 type.");
82  }
83 }
84 
85 template<class T>
86 inline hid_t hdf5Type();
87 
88 template<> inline hid_t hdf5Type<unsigned char>()
89  { return uintTypeHelper<unsigned char>(); }
90 template<> inline hid_t hdf5Type<unsigned short>()
91  { return uintTypeHelper<unsigned short>(); }
92 template<> inline hid_t hdf5Type<unsigned int>()
93  { return uintTypeHelper<unsigned int>(); }
94 template<> inline hid_t hdf5Type<unsigned long>()
95  { return uintTypeHelper<unsigned long>(); }
96 template<> inline hid_t hdf5Type<unsigned long long>()
97  { return uintTypeHelper<unsigned long long>(); }
98 template<> inline hid_t hdf5Type<char>()
99  { return uintTypeHelper<char>(); }
100 
101 template<> inline hid_t hdf5Type<signed char>()
102  { return intTypeHelper<signed char>(); }
103 template<> inline hid_t hdf5Type<short>()
104  { return intTypeHelper<short>(); }
105 template<> inline hid_t hdf5Type<int>()
106  { return intTypeHelper<int>(); }
107 template<> inline hid_t hdf5Type<long>()
108  { return intTypeHelper<long>(); }
109 template<> inline hid_t hdf5Type<long long>()
110  { return intTypeHelper<long long>(); }
111 
112 template<> inline hid_t hdf5Type<float>()
113  { return floatingTypeHelper<float>(); }
114 template<> inline hid_t hdf5Type<double>()
115  { return floatingTypeHelper<double>(); }
116 
117 enum class FileAccessMode {READ_ONLY, READ_WRITE};
118 enum class HDF5Version {DEFAULT_HDF5_VERSION, LATEST_HDF5_VERSION};
119 
120 hid_t createFile(const std::string&, HDF5Version = HDF5Version::DEFAULT_HDF5_VERSION);
121 hid_t openFile(const std::string&, FileAccessMode = FileAccessMode::READ_ONLY, HDF5Version = HDF5Version::DEFAULT_HDF5_VERSION);
122 void closeFile(const hid_t&);
123 
124 hid_t createGroup(const hid_t&, const std::string& groupName);
125 hid_t openGroup(const hid_t&, const std::string&, const bool =false);
126 void closeGroup(const hid_t&);
127 
128 template<class T>
129 void save(const hid_t&, const std::string&, std::initializer_list<std::size_t>, const T * const);
130 template<class T>
131 void save(const hid_t&, const std::string&, const T&);
132 
133 template<class T>
134 void load(const hid_t&, const std::string&, std::vector<std::size_t>&, std::vector<T>&);
135 template<class T>
136 void load(const hid_t&, const std::string&, std::vector<std::vector<T> >&);
137 template<class T>
138 void load(const hid_t&, const std::string&, T&);
139 
149 inline hid_t
151  const std::string& filename,
152  HDF5Version hdf5version
153 ) {
154  hid_t version = H5P_DEFAULT;
155  if(hdf5version == HDF5Version::LATEST_HDF5_VERSION) {
156  version = H5Pcreate(H5P_FILE_ACCESS);
157  H5Pset_libver_bounds(version, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST);
158  }
159  hid_t fileHandle = H5Fcreate(filename.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, version);
160  H5Pclose(version);
161  if(fileHandle < 0) {
162  throw std::runtime_error("Could not create HDF5 file: " + filename);
163  }
164  return fileHandle;
165 }
166 
177 inline hid_t
179  const std::string& filename,
180  FileAccessMode fileAccessMode,
181  HDF5Version hdf5version
182 ) {
183  hid_t access = H5F_ACC_RDONLY;
184  if(fileAccessMode == FileAccessMode::READ_WRITE) {
185  access = H5F_ACC_RDWR;
186  }
187 
188  hid_t version = H5P_DEFAULT;
189  if(hdf5version == HDF5Version::LATEST_HDF5_VERSION) {
190  version = H5Pcreate(H5P_FILE_ACCESS);
191  H5Pset_libver_bounds(version, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST);
192  }
193 
194  hid_t fileHandle = H5Fopen(filename.c_str(), access, version);
195  if(hdf5version == HDF5Version::LATEST_HDF5_VERSION) {
196  H5Pclose(version);
197  }
198  if(fileHandle < 0) {
199  throw std::runtime_error("Could not open HDF5 file: " + filename);
200  }
201  return fileHandle;
202 }
203 
210 inline void closeFile (
211  const hid_t& handle
212 ) {
213  H5Fclose(handle);
214 }
215 
224 inline hid_t
226  const hid_t& parentHandle,
227  const std::string& groupName
228 ) {
229  hid_t groupHandle = H5Gcreate(parentHandle, groupName.c_str(),
230  H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
231  if(groupHandle < 0) {
232  throw std::runtime_error("Could not create HDF5 group.");
233  }
234  return groupHandle;
235 }
236 
246 inline hid_t
248  const hid_t& parentHandle,
249  const std::string& groupName,
250  const bool createIfNonexistent
251 ) {
252  hid_t groupHandle;
253 
254  if(createIfNonexistent) {
255  H5E_auto2_t old_func;
256  void *old_client_data;
257  H5Eget_auto(H5E_DEFAULT, &old_func, &old_client_data);
258  H5Eset_auto(H5E_DEFAULT, NULL, NULL);
259  groupHandle = H5Gopen(parentHandle, groupName.c_str(), H5P_DEFAULT);
260  H5Eset_auto2(H5E_DEFAULT, old_func, old_client_data);
261  if(groupHandle != 0) {
262  groupHandle = createGroup(parentHandle, groupName);
263  }
264  if(groupHandle < 0) {
265  throw std::runtime_error("HDF5: Could not open or create HDF5 group " + groupName + ".");
266  }
267  } else {
268  groupHandle = H5Gopen(parentHandle, groupName.c_str(), H5P_DEFAULT);
269  if(groupHandle < 0) {
270  throw std::runtime_error("HDF5: Could not open HDF5 group " + groupName + ".");
271  }
272  }
273  return groupHandle;
274 }
275 
282 inline void
284  const hid_t& handle
285 ) {
286  H5Gclose(handle);
287 }
288 
296 template<class T>
297 void save(
298  const hid_t& parentHandle,
299  const std::string& datasetName,
300  const std::initializer_list<std::size_t> shape,
301  const T * const data
302 ) {
303  assert(parentHandle >= 0);
304  HandleCheck<ANDRES_GRAPH_HDF5_DEBUG> handleCheck;
305 
306  hid_t datatype, dataspace, dataset;
307  std::string sError;
308 
309  // build dataspace
310  datatype = H5Tcopy(hdf5Type<T>());
311  {
312  std::vector<hsize_t> storeShape(shape.begin(), shape.end());
313  dataspace = H5Screate_simple(storeShape.size(), &storeShape[0], NULL);
314  }
315  if(dataspace < 0) {
316  sError = "Dataspace creation failed.";
317  goto cleanupType;
318  }
319 
320  // create new dataset
321  dataset = H5Dcreate(parentHandle, datasetName.c_str(), datatype,
322  dataspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
323  if(dataset < 0) {
324  sError = "Dataset creation failed.";
325  goto cleanupSpace;
326  }
327 
328  if(H5Dwrite(dataset, datatype, H5S_ALL, H5S_ALL,H5P_DEFAULT, data)<0) {
329  sError = "Failed to write data to dataset.";
330  goto cleanupDataset;
331  }
332 
333 cleanupDataset:
334  H5Dclose(dataset);
335 cleanupSpace:
336  H5Sclose(dataspace);
337 cleanupType:
338  H5Tclose(datatype);
339  if(!sError.empty()) {
340  throw std::runtime_error("HDF5: Saving sataset '"+datasetName+"' failed: " + sError);
341  }
342 }
343 
350 template<class T>
351 void save(
352  const hid_t& parentHandle,
353  const std::string& datasetName,
354  const T& data
355 ) {
356  assert(parentHandle >= 0);
357  HandleCheck<ANDRES_GRAPH_HDF5_DEBUG> handleCheck;
358 
359  std::string sError;
360  hid_t datatype, dataspace, dataset;
361  // build dataspace
362  datatype = H5Tcopy(hdf5Type<T>());
363  dataspace = H5Screate(H5S_SCALAR);
364  if(dataspace < 0) {
365  sError = "Cannot create dataspace.";
366  goto cleanupType;
367  }
368 
369  // create new dataset
370  dataset = H5Dcreate(parentHandle, datasetName.c_str(), datatype,
371  dataspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
372  if(dataset < 0) {
373  sError = "Cannot create dataset.";
374  goto cleanupSpace;
375  }
376 
377  if(H5Dwrite(dataset, datatype, H5S_ALL, H5S_ALL,H5P_DEFAULT, &data)!=0) {
378  sError = "Cannot write data.";
379  }
380 
381  H5Dclose(dataset);
382 
383 cleanupSpace:
384  H5Sclose(dataspace);
385 cleanupType:
386  H5Tclose(datatype);
387  if(!sError.empty()) {
388  throw std::runtime_error(
389  "HDF5: Saving dataset '"+datasetName+"' failed: " + sError
390  );
391  }
392 }
393 
402 template<class T>
403 void load(
404  const hid_t& parentHandle,
405  const std::string& datasetName,
406  std::vector<std::size_t>& shape,
407  std::vector<T>& data
408 ) {
409  assert(parentHandle >= 0);
410  HandleCheck<ANDRES_GRAPH_HDF5_DEBUG> handleCheck;
411 
412  hid_t dataset, memspace, filespace, type, nativeType;
413  int ndims;
414  std::vector<hsize_t> storedShape;
415  std::string sError;
416  shape.clear();
417 
418  dataset = H5Dopen(parentHandle, datasetName.c_str(), H5P_DEFAULT);
419  if(dataset < 0) {
420  sError = "Cannot open dataset.";
421  goto errorCheck;
422  }
423  filespace = H5Dget_space(dataset);
424  type = H5Dget_type(dataset);
425  nativeType = H5Tget_native_type(type, H5T_DIR_DESCEND);
426  if(!H5Tequal(nativeType, hdf5Type<T>())) {
427  sError = "Requested data type does not matched stored one.";
428  goto cleanupDataset;
429  }
430 
431  // Retrieve dimensions
432  ndims = H5Sget_simple_extent_ndims(filespace);
433  if(ndims<=0) {
434  sError = ndims == 0?"Dataset is a scalar.":"Cannot get number of dimensions.";
435  goto cleanupDataset;
436  }
437  storedShape.resize(ndims);
438  if(H5Sget_simple_extent_dims(filespace, &storedShape[0], NULL)<0) {
439  sError = "Cannot get simple extent dimensions.";
440  goto cleanupDataset;
441  }
442  memspace = H5Screate_simple(ndims, &storedShape[0], NULL);
443  if(memspace<=0) {
444  sError = "Could not allocate memory space.";
445  goto cleanupTypes;
446  }
447 
448  // Read data
449  {
450  std::size_t numberOfElements = 1;
451  for(std::size_t i=0;i<storedShape.size();++i)
452  numberOfElements *= storedShape[i];
453  data.resize(numberOfElements);
454  }
455  if(H5Dread(dataset, nativeType, memspace, filespace,H5P_DEFAULT, &data[0])!=0) {
456  sError = "Could not read data from dataset.";
457  goto cleanupDataset;
458  }
459  shape.assign(storedShape.begin(),storedShape.end());
460 
461 cleanupDataset:
462  H5Dclose(dataset);
463  H5Sclose(memspace);
464 cleanupTypes:
465  H5Tclose(nativeType);
466  H5Tclose(type);
467  H5Sclose(filespace);
468 errorCheck:
469  if(!sError.empty()) {
470  throw std::runtime_error("HDF5: Loading dataset '"+datasetName+"' failed: "+sError);
471  }
472 }
473 
482 template<class T>
483 void load(
484  const hid_t& parentHandle,
485  const std::string& datasetName,
486  T& data
487 ) {
488  assert(parentHandle >= 0);
489  HandleCheck<ANDRES_GRAPH_HDF5_DEBUG> handleCheck;
490 
491  std::string sError;
492  hid_t dataset, filespace, type, nativeType, memspace;
493  int ndims;
494  dataset = H5Dopen(parentHandle, datasetName.c_str(), H5P_DEFAULT);
495  if(dataset < 0) {
496  sError = "Cannot open dataset.";
497  goto error;
498  }
499  filespace = H5Dget_space(dataset);
500  type = H5Dget_type(dataset);
501  nativeType = H5Tget_native_type(type, H5T_DIR_DESCEND);
502  if(!H5Tequal(nativeType, hdf5Type<T>())) {
503  sError = "Stored data type does not match the one requested.";
504  goto cleanupTypes;
505  }
506  ndims = H5Sget_simple_extent_ndims(filespace);
507  if(ndims!=0) {
508  sError = "Dataset dimension is not zero.";
509  goto cleanupTypes;
510  }
511  memspace = H5Screate(H5S_SCALAR);
512  if(memspace<0) {
513  sError = "Failed to create memory dataspace.";
514  goto cleanupAll;
515  }
516  if(H5Dread(dataset, nativeType, memspace, filespace,H5P_DEFAULT, &data)<0) {
517  sError = "Failed to write scalar.";
518  goto cleanupAll;
519  }
520 
521 cleanupAll:
522  H5Sclose(memspace);
523 cleanupTypes:
524  H5Tclose(nativeType);
525  H5Tclose(type);
526  H5Sclose(filespace);
527  H5Dclose(dataset);
528 error:
529  if(!sError.empty()) {
530  throw std::runtime_error(
531  "HDF5:Loading scalar from dataset '"+datasetName+"' failed: " + sError
532  );
533  }
534 }
535 
536 
545 template<>
546 inline void
548  const hid_t& parentHandle,
549  const std::string& datasetName,
550  std::string& data
551 ) {
552  assert(parentHandle>0);
553  HandleCheck<ANDRES_GRAPH_HDF5_DEBUG> handleCheck;
554 
555  std::string sError;
556 
557  hid_t dataSet, fileType, dataSpace, memType;
558  hsize_t stringSize;
559 
560  dataSet = H5Dopen (parentHandle, datasetName.c_str(), H5P_DEFAULT);
561  fileType = H5Dget_type(dataSet);
562  dataSpace = H5Dget_space(dataSet);
563  // Verify we opened an 1 dimensional array.
564  {
565  hsize_t dims[1];
566  hsize_t ndims = H5Sget_simple_extent_dims(dataSpace, NULL, NULL);
567  if(ndims!=1) {
568  sError = "Dataset spans more than 1 dimensions.";
569  goto cleanupTypes;
570  }
571  ndims = H5Sget_simple_extent_dims(dataSpace, dims, NULL);
572  // Assume only 1 string saved
573  if(dims[0]!=1) {
574  sError = "Dataset contains more than 1 elements.";
575  goto cleanupTypes;
576  }
577  }
578  // Verify this is not a variable dimension array
579  {
580  htri_t isVariable = H5Tis_variable_str(fileType);
581  if(isVariable) {
582  sError = "String type is unsupported.";
583  goto cleanupTypes;
584  //const hid_t memType = H5Tget_native_type(fileType, H5T_DIR_ASCEND);
585  //const hid_t mc = H5Tcopy(memType);
586  // something like: char **ptrBuff = (char **) malloc(dim[0]*sizeof(char *));
587  //status = H5Dread (dataSet, memType, H5S_ALL, H5S_ALL, H5P_DEFAULT, &ptrBuf);
588  // Use
589  //status = H5Dvlen_reclaim (memType, dataSpace, H5P_DEFAULT, ptrBuf);
590  }
591  }
592  stringSize = H5Tget_size(fileType);
593  memType = H5Tcopy(H5T_C_S1);
594  H5Tset_size(memType,stringSize);
595  {
596  std::vector<char> buf(stringSize);
597  herr_t status = H5Dread (dataSet, memType, H5S_ALL, H5S_ALL, H5P_DEFAULT, &buf[0]);
598  if(status!=0) {
599  sError = "Could not read string data.";
600  goto cleanupAll;
601  }
602  data.assign(&buf[0]);
603  }
604 
605 cleanupAll:
606  H5Tclose (memType);
607 cleanupTypes:
608  H5Sclose (dataSpace);
609  H5Tclose (fileType);
610  H5Dclose (dataSet);
611  if(!sError.empty()) {
612  throw std::runtime_error(
613  "HDF5: Loading string from dataset '"+datasetName+"' failed: "+sError
614  );
615  }
616 }
617 
618 
619 } //namespace hdf
620 } //namespace graph
621 } //namespace andres
622 
623 #endif // #ifndef ANDRES_GRAPH_HDF5_HXX