GNU Octave  3.8.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
ls-hdf5.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 1996-2013 John W. Eaton
4 
5 This file is part of Octave.
6 
7 Octave is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Octave; see the file COPYING. If not, see
19 <http://www.gnu.org/licenses/>.
20 
21 */
22 
23 // Author: Steven G. Johnson <stevenj@alum.mit.edu>
24 
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28 
29 #if defined (HAVE_HDF5)
30 
31 #include <cfloat>
32 #include <cstring>
33 #include <cctype>
34 
35 #include <fstream>
36 #include <iomanip>
37 #include <iostream>
38 #include <string>
39 #include <vector>
40 
41 #include "byte-swap.h"
42 #include "data-conv.h"
43 #include "file-ops.h"
44 #include "glob-match.h"
45 #include "lo-mappers.h"
46 #include "mach-info.h"
47 #include "oct-env.h"
48 #include "oct-time.h"
49 #include "quit.h"
50 #include "str-vec.h"
51 #include "oct-locbuf.h"
52 
53 #include "Cell.h"
54 #include "defun.h"
55 #include "error.h"
56 #include "gripes.h"
57 #include "load-save.h"
58 #include "oct-obj.h"
59 #include "oct-map.h"
60 #include "ov-cell.h"
61 #include "pager.h"
62 #include "pt-exp.h"
63 #include "sysdep.h"
64 #include "unwind-prot.h"
65 #include "utils.h"
66 #include "variables.h"
67 #include "version.h"
68 #include "dMatrix.h"
69 #include "ov-lazy-idx.h"
70 
71 #include "ls-utils.h"
72 #include "ls-hdf5.h"
73 
74 static std::string
75 make_valid_identifier (const std::string& nm)
76 {
77  std::string retval;
78 
79  size_t nm_len = nm.length ();
80 
81  if (nm_len > 0)
82  {
83  if (! isalpha (nm[0]))
84  retval += '_';
85 
86  for (size_t i = 0; i < nm_len; i++)
87  {
88  char c = nm[i];
89  retval += (isalnum (c) || c == '_') ? c : '_';
90  }
91  }
92 
93  return retval;
94 }
95 
96 // Define this to 1 if/when HDF5 supports automatic conversion between
97 // integer and floating-point binary data:
98 #define HAVE_HDF5_INT2FLOAT_CONVERSIONS 0
99 
100 // Given two compound types t1 and t2, determine whether they
101 // are compatible for reading/writing. This function only
102 // works for non-nested types composed of simple elements (ints, floats...),
103 // which is all we need it for
104 
105 bool
106 hdf5_types_compatible (hid_t t1, hid_t t2)
107 {
108  int n;
109  if ((n = H5Tget_nmembers (t1)) != H5Tget_nmembers (t2))
110  return false;
111 
112  for (int i = 0; i < n; ++i)
113  {
114  hid_t mt1 = H5Tget_member_type (t1, i);
115  hid_t mt2 = H5Tget_member_type (t2, i);
116 
117  if (H5Tget_class (mt1) != H5Tget_class (mt2))
118  return false;
119 
120  H5Tclose (mt2);
121  H5Tclose (mt1);
122  }
123 
124  return true;
125 }
126 
127 // Return true if loc_id has the attribute named attr_name, and false
128 // otherwise.
129 
130 bool
131 hdf5_check_attr (hid_t loc_id, const char *attr_name)
132 {
133  bool retval = false;
134 
135  // we have to pull some shenanigans here to make sure
136  // HDF5 doesn't print out all sorts of error messages if we
137  // call H5Aopen for a non-existing attribute
138 
139  H5E_auto_t err_func;
140  void *err_func_data;
141 
142  // turn off error reporting temporarily, but save the error
143  // reporting function:
144 
145 #if HAVE_HDF5_18
146  H5Eget_auto (H5E_DEFAULT, &err_func, &err_func_data);
147  H5Eset_auto (H5E_DEFAULT, 0, 0);
148 #else
149  H5Eget_auto (&err_func, &err_func_data);
150  H5Eset_auto (0, 0);
151 #endif
152 
153  hid_t attr_id = H5Aopen_name (loc_id, attr_name);
154 
155  if (attr_id >= 0)
156  {
157  // successful
158  retval = true;
159  H5Aclose (attr_id);
160  }
161 
162  // restore error reporting:
163 #if HAVE_HDF5_18
164  H5Eset_auto (H5E_DEFAULT, err_func, err_func_data);
165 #else
166  H5Eset_auto (err_func, err_func_data);
167 #endif
168  return retval;
169 }
170 
171 bool
172 hdf5_get_scalar_attr (hid_t loc_id, hid_t type_id,
173  const char *attr_name, void *buf)
174 {
175  bool retval = false;
176 
177  // we have to pull some shenanigans here to make sure
178  // HDF5 doesn't print out all sorts of error messages if we
179  // call H5Aopen for a non-existing attribute
180 
181  H5E_auto_t err_func;
182  void *err_func_data;
183 
184  // turn off error reporting temporarily, but save the error
185  // reporting function:
186 
187 #if HAVE_HDF5_18
188  H5Eget_auto (H5E_DEFAULT, &err_func, &err_func_data);
189  H5Eset_auto (H5E_DEFAULT, 0, 0);
190 #else
191  H5Eget_auto (&err_func, &err_func_data);
192  H5Eset_auto (0, 0);
193 #endif
194 
195  hid_t attr_id = H5Aopen_name (loc_id, attr_name);
196 
197  if (attr_id >= 0)
198  {
199  hid_t space_id = H5Aget_space (attr_id);
200 
201  hsize_t rank = H5Sget_simple_extent_ndims (space_id);
202 
203  if (rank == 0)
204  retval = H5Aread (attr_id, type_id, buf) >= 0;
205  H5Aclose (attr_id);
206  }
207 
208  // restore error reporting:
209 #if HAVE_HDF5_18
210  H5Eset_auto (H5E_DEFAULT, err_func, err_func_data);
211 #else
212  H5Eset_auto (err_func, err_func_data);
213 #endif
214  return retval;
215 }
216 
217 
218 
219 
220 // The following subroutines creates an HDF5 representations of the way
221 // we will store Octave complex types (pairs of floating-point numbers).
222 // NUM_TYPE is the HDF5 numeric type to use for storage (e.g.
223 // H5T_NATIVE_DOUBLE to save as 'double'). Note that any necessary
224 // conversions are handled automatically by HDF5.
225 
226 hid_t
227 hdf5_make_complex_type (hid_t num_type)
228 {
229  hid_t type_id = H5Tcreate (H5T_COMPOUND, sizeof (double) * 2);
230 
231  H5Tinsert (type_id, "real", 0 * sizeof (double), num_type);
232  H5Tinsert (type_id, "imag", 1 * sizeof (double), num_type);
233 
234  return type_id;
235 }
236 
237 // This function is designed to be passed to H5Giterate, which calls it
238 // on each data item in an HDF5 file. For the item whose name is NAME in
239 // the group GROUP_ID, this function sets dv->tc to an Octave representation
240 // of that item. (dv must be a pointer to hdf5_callback_data.) (It also
241 // sets the other fields of dv).
242 //
243 // It returns 1 on success (in which case H5Giterate stops and returns),
244 // -1 on error, and 0 to tell H5Giterate to continue on to the next item
245 // (e.g. if NAME was a data type we don't recognize).
246 
247 herr_t
248 hdf5_read_next_data (hid_t group_id, const char *name, void *dv)
249 {
250  hdf5_callback_data *d = static_cast <hdf5_callback_data *> (dv);
251  hid_t type_id = -1;
252  hid_t type_class_id = -1;
253  hid_t data_id = -1;
254  hid_t subgroup_id = -1;
255  hid_t space_id = -1;;
256 
257  H5G_stat_t info;
258  herr_t retval = 0;
259  bool ident_valid = valid_identifier (name);
260 
261  std::string vname = name;
262 
263  // Allow identifiers as all digits so we can load lists saved by
264  // earlier versions of Octave.
265 
266  if (! ident_valid )
267  {
268  // fix the identifier, replacing invalid chars with underscores
269  vname = make_valid_identifier (vname);
270 
271  // check again (in case vname was null, empty, or some such thing):
272  ident_valid = valid_identifier (vname);
273  }
274 
275  H5Gget_objinfo (group_id, name, 1, &info);
276 
277  if (info.type == H5G_GROUP && ident_valid)
278  {
279 #if HAVE_HDF5_18
280  subgroup_id = H5Gopen (group_id, name, H5P_DEFAULT);
281 #else
282  subgroup_id = H5Gopen (group_id, name);
283 #endif
284 
285  if (subgroup_id < 0)
286  {
287  retval = subgroup_id;
288  goto done;
289  }
290 
291  if (hdf5_check_attr (subgroup_id, "OCTAVE_NEW_FORMAT"))
292  {
293 #if HAVE_HDF5_18
294  data_id = H5Dopen (subgroup_id, "type", H5P_DEFAULT);
295 #else
296  data_id = H5Dopen (subgroup_id, "type");
297 #endif
298 
299  if (data_id < 0)
300  {
301  retval = data_id;
302  goto done;
303  }
304 
305  type_id = H5Dget_type (data_id);
306 
307  type_class_id = H5Tget_class (type_id);
308 
309  if (type_class_id != H5T_STRING)
310  goto done;
311 
312  space_id = H5Dget_space (data_id);
313  hsize_t rank = H5Sget_simple_extent_ndims (space_id);
314 
315  if (rank != 0)
316  goto done;
317 
318  int slen = H5Tget_size (type_id);
319  if (slen < 0)
320  goto done;
321 
322  OCTAVE_LOCAL_BUFFER (char, typ, slen);
323 
324  // create datatype for (null-terminated) string to read into:
325  hid_t st_id = H5Tcopy (H5T_C_S1);
326  H5Tset_size (st_id, slen);
327 
328  if (H5Dread (data_id, st_id, H5S_ALL, H5S_ALL, H5P_DEFAULT,
329  typ) < 0)
330  goto done;
331 
332  H5Tclose (st_id);
333  H5Dclose (data_id);
334 
336 
337  retval = (d->tc.load_hdf5 (subgroup_id, "value") ? 1 : -1);
338 
339  // check for OCTAVE_GLOBAL attribute:
340  d->global = hdf5_check_attr (subgroup_id, "OCTAVE_GLOBAL");
341 
342  H5Gclose (subgroup_id);
343  }
344  else
345  {
346  // an HDF5 group is treated as an octave structure by
347  // default (since that preserves name information), and an
348  // octave list otherwise.
349 
350  if (hdf5_check_attr (subgroup_id, "OCTAVE_LIST"))
352  else
353  d->tc = octave_value_typeinfo::lookup_type ("struct");
354 
355  // check for OCTAVE_GLOBAL attribute:
356  d->global = hdf5_check_attr (subgroup_id, "OCTAVE_GLOBAL");
357 
358  H5Gclose (subgroup_id);
359 
360  retval = (d->tc.load_hdf5 (group_id, name) ? 1 : -1);
361  }
362 
363  }
364  else if (info.type == H5G_DATASET && ident_valid)
365  {
366  // For backwards compatiability.
367 #if HAVE_HDF5_18
368  data_id = H5Dopen (group_id, name, H5P_DEFAULT);
369 #else
370  data_id = H5Dopen (group_id, name);
371 #endif
372 
373  if (data_id < 0)
374  {
375  retval = data_id;
376  goto done;
377  }
378 
379  type_id = H5Dget_type (data_id);
380 
381  type_class_id = H5Tget_class (type_id);
382 
383  if (type_class_id == H5T_FLOAT)
384  {
385  space_id = H5Dget_space (data_id);
386 
387  hsize_t rank = H5Sget_simple_extent_ndims (space_id);
388 
389  if (rank == 0)
390  d->tc = octave_value_typeinfo::lookup_type ("scalar");
391  else
392  d->tc = octave_value_typeinfo::lookup_type ("matrix");
393 
394  H5Sclose (space_id);
395  }
396  else if (type_class_id == H5T_INTEGER)
397  {
398  // What integer type do we really have..
399  std::string int_typ;
400 #ifdef HAVE_H5T_GET_NATIVE_TYPE
401  // FIXME: test this code and activated with an autoconf
402  // test!! It is also incorrect for 64-bit indexing!!
403 
404  switch (H5Tget_native_type (type_id, H5T_DIR_ASCEND))
405  {
406  case H5T_NATIVE_CHAR:
407  int_typ = "int8 ";
408  break;
409 
410  case H5T_NATIVE_SHORT:
411  int_typ = "int16 ";
412  break;
413 
414  case H5T_NATIVE_INT:
415  case H5T_NATIVE_LONG:
416  int_typ = "int32 ";
417  break;
418 
419  case H5T_NATIVE_LLONG:
420  int_typ = "int64 ";
421  break;
422 
423  case H5T_NATIVE_UCHAR:
424  int_typ = "uint8 ";
425  break;
426 
427  case H5T_NATIVE_USHORT:
428  int_typ = "uint16 ";
429  break;
430 
431  case H5T_NATIVE_UINT:
432  case H5T_NATIVE_ULONG:
433  int_typ = "uint32 ";
434  break;
435 
436  case H5T_NATIVE_ULLONG:
437  int_typ = "uint64 ";
438  break;
439  }
440 #else
441  hid_t int_sign = H5Tget_sign (type_id);
442 
443  if (int_sign == H5T_SGN_ERROR)
444  warning ("load: can't read '%s' (unknown datatype)", name);
445  else
446  {
447  if (int_sign == H5T_SGN_NONE)
448  int_typ.append ("u");
449  int_typ.append ("int");
450 
451  int slen = H5Tget_size (type_id);
452  if (slen < 0)
453  warning ("load: can't read '%s' (unknown datatype)", name);
454  else
455  {
456  switch (slen)
457  {
458  case 1:
459  int_typ.append ("8 ");
460  break;
461 
462  case 2:
463  int_typ.append ("16 ");
464  break;
465 
466  case 4:
467  int_typ.append ("32 ");
468  break;
469 
470  case 8:
471  int_typ.append ("64 ");
472  break;
473 
474  default:
475  warning ("load: can't read '%s' (unknown datatype)",
476  name);
477  int_typ = "";
478  break;
479  }
480  }
481  }
482 #endif
483  if (int_typ == "")
484  warning ("load: can't read '%s' (unknown datatype)", name);
485  else
486  {
487  // Matrix or scalar?
488  space_id = H5Dget_space (data_id);
489 
490  hsize_t rank = H5Sget_simple_extent_ndims (space_id);
491 
492  if (rank == 0)
493  int_typ.append ("scalar");
494  else
495  int_typ.append ("matrix");
496 
497  d->tc = octave_value_typeinfo::lookup_type (int_typ);
498  H5Sclose (space_id);
499  }
500  }
501  else if (type_class_id == H5T_STRING)
502  d->tc = octave_value_typeinfo::lookup_type ("string");
503  else if (type_class_id == H5T_COMPOUND)
504  {
505  hid_t complex_type = hdf5_make_complex_type (H5T_NATIVE_DOUBLE);
506 
507  if (hdf5_types_compatible (type_id, complex_type))
508  {
509  // read complex matrix or scalar variable
510  space_id = H5Dget_space (data_id);
511  hsize_t rank = H5Sget_simple_extent_ndims (space_id);
512 
513  if (rank == 0)
514  d->tc = octave_value_typeinfo::lookup_type ("complex scalar");
515  else
516  d->tc = octave_value_typeinfo::lookup_type ("complex matrix");
517 
518  H5Sclose (space_id);
519  }
520  else
521  // Assume that if its not complex its a range. If its not
522  // it'll be rejected later in the range code
523  d->tc = octave_value_typeinfo::lookup_type ("range");
524 
525  H5Tclose (complex_type);
526  }
527  else
528  {
529  warning ("load: can't read '%s' (unknown datatype)", name);
530  retval = 0; // unknown datatype; skip
531  }
532 
533  // check for OCTAVE_GLOBAL attribute:
534  d->global = hdf5_check_attr (data_id, "OCTAVE_GLOBAL");
535 
536  H5Tclose (type_id);
537  H5Dclose (data_id);
538 
539  retval = (d->tc.load_hdf5 (group_id, name) ? 1 : -1);
540  }
541 
542  if (!ident_valid)
543  {
544  // should we attempt to handle invalid identifiers by converting
545  // bad characters to '_', say?
546  warning ("load: skipping invalid identifier '%s' in hdf5 file",
547  name);
548  }
549 
550 done:
551  if (retval < 0)
552  error ("load: error while reading hdf5 item %s", name);
553 
554  if (retval > 0)
555  {
556  // get documentation string, if any:
557  int comment_length = H5Gget_comment (group_id, name, 0, 0);
558 
559  if (comment_length > 1)
560  {
561  OCTAVE_LOCAL_BUFFER (char, tdoc, comment_length);
562  H5Gget_comment (group_id, name, comment_length, tdoc);
563  d->doc = tdoc;
564  }
565  else if (vname != name)
566  {
567  // the name was changed; store the original name
568  // as the documentation string:
569  d->doc = name;
570  }
571 
572  // copy name (actually, vname):
573  d->name = vname;
574  }
575 
576  return retval;
577 }
578 
579 // Read the next Octave variable from the stream IS, which must really be
580 // an hdf5_ifstream. Return the variable value in tc, its doc string
581 // in doc, and whether it is global in global. The return value is
582 // the name of the variable, or NULL if none were found or there was
583 // and error.
584 std::string
585 read_hdf5_data (std::istream& is, const std::string& /* filename */,
586  bool& global, octave_value& tc, std::string& doc,
587  const string_vector& argv, int argv_idx, int argc)
588 {
589  std::string retval;
590 
591  doc.resize (0);
592 
593  hdf5_ifstream& hs = dynamic_cast<hdf5_ifstream&> (is);
595 
596  herr_t H5Giterate_retval = -1;
597 
598  hsize_t num_obj = 0;
599 #if HAVE_HDF5_18
600  hid_t group_id = H5Gopen (hs.file_id, "/", H5P_DEFAULT);
601 #else
602  hid_t group_id = H5Gopen (hs.file_id, "/");
603 #endif
604  H5Gget_num_objs (group_id, &num_obj);
605  H5Gclose (group_id);
606 
607  // For large datasets and out-of-core functionality,
608  // check if only parts of the data is requested
609  bool load_named_vars = argv_idx < argc;
610  while (load_named_vars && hs.current_item < static_cast<int> (num_obj))
611  {
612  std::vector<char> var_name;
613  bool found = false;
614  size_t len = 0;
615 
616  len = H5Gget_objname_by_idx (hs.file_id, hs.current_item, 0, 0);
617  var_name.resize (len+1);
618  H5Gget_objname_by_idx( hs.file_id, hs.current_item, &var_name[0], len+1);
619 
620  for (int i = argv_idx; i < argc; i++)
621  {
622  glob_match pattern (argv[i]);
623  if (pattern.match (std::string (&var_name[0])))
624  {
625  found = true;
626  break;
627  }
628  }
629 
630  if (found)
631  break;
632 
633  hs.current_item++;
634  }
635 
636 
637  if (hs.current_item < static_cast<int> (num_obj))
638  H5Giterate_retval = H5Giterate (hs.file_id, "/", &hs.current_item,
639  hdf5_read_next_data, &d);
640 
641  if (H5Giterate_retval > 0)
642  {
643  global = d.global;
644  tc = d.tc;
645  doc = d.doc;
646  }
647  else
648  {
649  // an error occurred (H5Giterate_retval < 0) or there are no
650  // more datasets print an error message if retval < 0?
651  // hdf5_read_next_data already printed one, probably.
652  }
653 
654  if (! d.name.empty ())
655  retval = d.name;
656 
657  return retval;
658 }
659 
660 // Add an attribute named attr_name to loc_id (a simple scalar
661 // attribute with value 1). Return value is >= 0 on success.
662 herr_t
663 hdf5_add_attr (hid_t loc_id, const char *attr_name)
664 {
665  herr_t retval = 0;
666 
667  hid_t as_id = H5Screate (H5S_SCALAR);
668 
669  if (as_id >= 0)
670  {
671 #if HAVE_HDF5_18
672  hid_t a_id = H5Acreate (loc_id, attr_name, H5T_NATIVE_UCHAR,
673  as_id, H5P_DEFAULT, H5P_DEFAULT);
674 #else
675  hid_t a_id = H5Acreate (loc_id, attr_name,
676  H5T_NATIVE_UCHAR, as_id, H5P_DEFAULT);
677 #endif
678  if (a_id >= 0)
679  {
680  unsigned char attr_val = 1;
681 
682  retval = H5Awrite (a_id, H5T_NATIVE_UCHAR, &attr_val);
683 
684  H5Aclose (a_id);
685  }
686  else
687  retval = a_id;
688 
689  H5Sclose (as_id);
690  }
691  else
692  retval = as_id;
693 
694  return retval;
695 }
696 
697 herr_t
698 hdf5_add_scalar_attr (hid_t loc_id, hid_t type_id,
699  const char *attr_name, void *buf)
700 {
701  herr_t retval = 0;
702 
703  hid_t as_id = H5Screate (H5S_SCALAR);
704 
705  if (as_id >= 0)
706  {
707 #if HAVE_HDF5_18
708  hid_t a_id = H5Acreate (loc_id, attr_name, type_id,
709  as_id, H5P_DEFAULT, H5P_DEFAULT);
710 #else
711  hid_t a_id = H5Acreate (loc_id, attr_name,
712  type_id, as_id, H5P_DEFAULT);
713 #endif
714  if (a_id >= 0)
715  {
716  retval = H5Awrite (a_id, type_id, buf);
717 
718  H5Aclose (a_id);
719  }
720  else
721  retval = a_id;
722 
723  H5Sclose (as_id);
724  }
725  else
726  retval = as_id;
727 
728  return retval;
729 }
730 
731 // Save an empty matrix, if needed. Returns
732 // > 0 Saved empty matrix
733 // = 0 Not an empty matrix; did nothing
734 // < 0 Error condition
735 int
736 save_hdf5_empty (hid_t loc_id, const char *name, const dim_vector d)
737 {
738  hsize_t sz = d.length ();
740  bool empty = false;
741  hid_t space_hid = -1, data_hid = -1;
742  int retval;
743  for (hsize_t i = 0; i < sz; i++)
744  {
745  dims[i] = d(i);
746  if (dims[i] < 1)
747  empty = true;
748  }
749 
750  if (!empty)
751  return 0;
752 
753  space_hid = H5Screate_simple (1, &sz, 0);
754  if (space_hid < 0) return space_hid;
755 #if HAVE_HDF5_18
756  data_hid = H5Dcreate (loc_id, name, H5T_NATIVE_IDX, space_hid,
757  H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
758 #else
759  data_hid = H5Dcreate (loc_id, name, H5T_NATIVE_IDX, space_hid,
760  H5P_DEFAULT);
761 #endif
762  if (data_hid < 0)
763  {
764  H5Sclose (space_hid);
765  return data_hid;
766  }
767 
768  retval = H5Dwrite (data_hid, H5T_NATIVE_IDX, H5S_ALL, H5S_ALL,
769  H5P_DEFAULT, dims) >= 0;
770 
771  H5Dclose (data_hid);
772  H5Sclose (space_hid);
773 
774  if (retval >= 0)
775  retval = hdf5_add_attr (loc_id, "OCTAVE_EMPTY_MATRIX");
776 
777  return (retval == 0 ? 1 : retval);
778 }
779 
780 // Load an empty matrix, if needed. Returns
781 // > 0 loaded empty matrix, dimensions returned
782 // = 0 Not an empty matrix; did nothing
783 // < 0 Error condition
784 int
785 load_hdf5_empty (hid_t loc_id, const char *name, dim_vector &d)
786 {
787  if (! hdf5_check_attr (loc_id, "OCTAVE_EMPTY_MATRIX"))
788  return 0;
789 
790  hsize_t hdims, maxdims;
791 #if HAVE_HDF5_18
792  hid_t data_hid = H5Dopen (loc_id, name, H5P_DEFAULT);
793 #else
794  hid_t data_hid = H5Dopen (loc_id, name);
795 #endif
796  hid_t space_id = H5Dget_space (data_hid);
797  H5Sget_simple_extent_dims (space_id, &hdims, &maxdims);
798  int retval;
799 
800  OCTAVE_LOCAL_BUFFER (octave_idx_type, dims, hdims);
801 
802  retval = H5Dread (data_hid, H5T_NATIVE_IDX, H5S_ALL, H5S_ALL,
803  H5P_DEFAULT, dims);
804  if (retval >= 0)
805  {
806  d.resize (hdims);
807  for (hsize_t i = 0; i < hdims; i++)
808  d(i) = dims[i];
809  }
810 
811  H5Sclose (space_id);
812  H5Dclose (data_hid);
813 
814  return (retval == 0 ? hdims : retval);
815 }
816 
817 // save_type_to_hdf5 is not currently used, since hdf5 doesn't yet support
818 // automatic float<->integer conversions:
819 
820 #if HAVE_HDF5_INT2FLOAT_CONVERSIONS
821 
822 // return the HDF5 type id corresponding to the Octave save_type
823 
824 hid_t
825 save_type_to_hdf5 (save_type st)
826 {
827  switch (st)
828  {
829  case LS_U_CHAR:
830  return H5T_NATIVE_UCHAR;
831 
832  case LS_U_SHORT:
833  return H5T_NATIVE_USHORT;
834 
835  case LS_U_INT:
836  return H5T_NATIVE_UINT;
837 
838  case LS_CHAR:
839  return H5T_NATIVE_CHAR;
840 
841  case LS_SHORT:
842  return H5T_NATIVE_SHORT;
843 
844  case LS_INT:
845  return H5T_NATIVE_INT;
846 
847  case LS_FLOAT:
848  return H5T_NATIVE_FLOAT;
849 
850  case LS_DOUBLE:
851  default:
852  return H5T_NATIVE_DOUBLE;
853  }
854 }
855 #endif /* HAVE_HDF5_INT2FLOAT_CONVERSIONS */
856 
857 // Add the data from TC to the HDF5 location loc_id, which could
858 // be either a file or a group within a file. Return true if
859 // successful. This function calls itself recursively for lists
860 // (stored as HDF5 groups).
861 
862 bool
863 add_hdf5_data (hid_t loc_id, const octave_value& tc,
864  const std::string& name, const std::string& doc,
865  bool mark_as_global, bool save_as_floats)
866 {
867  hsize_t dims[3];
868  hid_t type_id = -1, space_id = -1, data_id = -1, data_type_id = -1;
869  bool retval = false;
870  octave_value val = tc;
871  // FIXME: diagonal & permutation matrices currently don't know how to save
872  // themselves, so we convert them first to normal matrices using A = A(:,:).
873  // This is a temporary hack.
874  if (val.is_diag_matrix () || val.is_perm_matrix ()
876  val = val.full_value ();
877 
878  std::string t = val.type_name ();
879 #if HAVE_HDF5_18
880  data_id = H5Gcreate (loc_id, name.c_str (), H5P_DEFAULT, H5P_DEFAULT,
881  H5P_DEFAULT);
882 #else
883  data_id = H5Gcreate (loc_id, name.c_str (), 0);
884 #endif
885  if (data_id < 0)
886  goto error_cleanup;
887 
888  // attach the type of the variable
889  type_id = H5Tcopy (H5T_C_S1); H5Tset_size (type_id, t.length () + 1);
890  if (type_id < 0)
891  goto error_cleanup;
892 
893  dims[0] = 0;
894  space_id = H5Screate_simple (0 , dims, 0);
895  if (space_id < 0)
896  goto error_cleanup;
897 #if HAVE_HDF5_18
898  data_type_id = H5Dcreate (data_id, "type", type_id, space_id,
899  H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
900 #else
901  data_type_id = H5Dcreate (data_id, "type", type_id, space_id, H5P_DEFAULT);
902 #endif
903  if (data_type_id < 0 || H5Dwrite (data_type_id, type_id, H5S_ALL, H5S_ALL,
904  H5P_DEFAULT, t.c_str ()) < 0)
905  goto error_cleanup;
906 
907  // Now call the real function to save the variable
908  retval = val.save_hdf5 (data_id, "value", save_as_floats);
909 
910  // attach doc string as comment:
911  if (retval && doc.length () > 0
912  && H5Gset_comment (loc_id, name.c_str (), doc.c_str ()) < 0)
913  retval = false;
914 
915  // if it's global, add an attribute "OCTAVE_GLOBAL" with value 1
916  if (retval && mark_as_global)
917  retval = hdf5_add_attr (data_id, "OCTAVE_GLOBAL") >= 0;
918 
919  // We are saving in the new variable format, so mark it
920  if (retval)
921  retval = hdf5_add_attr (data_id, "OCTAVE_NEW_FORMAT") >= 0;
922 
923 error_cleanup:
924 
925  if (data_type_id >= 0)
926  H5Dclose (data_type_id);
927 
928  if (type_id >= 0)
929  H5Tclose (type_id);
930 
931  if (space_id >= 0)
932  H5Sclose (space_id);
933 
934  if (data_id >= 0)
935  H5Gclose (data_id);
936 
937  if (! retval)
938  error ("save: error while writing '%s' to hdf5 file", name.c_str ());
939 
940  return retval;
941 }
942 
943 // Write data from TC in HDF5 (binary) format to the stream OS,
944 // which must be an hdf5_ofstream, returning true on success.
945 
946 bool
947 save_hdf5_data (std::ostream& os, const octave_value& tc,
948  const std::string& name, const std::string& doc,
949  bool mark_as_global, bool save_as_floats)
950 {
951  hdf5_ofstream& hs = dynamic_cast<hdf5_ofstream&> (os);
952 
953  return add_hdf5_data (hs.file_id, tc, name, doc,
954  mark_as_global, save_as_floats);
955 }
956 
957 #endif