File: | libinterp/corefcn/ls-hdf5.cc |
Location: | line 530, column 11 |
Description: | Value stored to 'retval' is never read |
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_H1 |
26 | #include <config.h> |
27 | #endif |
28 | |
29 | #if defined (HAVE_HDF51) |
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_CONVERSIONS0 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_tH5E_auto2_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_181 |
146 | H5Eget_autoH5Eget_auto2 (H5E_DEFAULT0, &err_func, &err_func_data); |
147 | H5Eset_autoH5Eset_auto2 (H5E_DEFAULT0, 0, 0); |
148 | #else |
149 | H5Eget_autoH5Eget_auto2 (&err_func, &err_func_data); |
150 | H5Eset_autoH5Eset_auto2 (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_181 |
164 | H5Eset_autoH5Eset_auto2 (H5E_DEFAULT0, err_func, err_func_data); |
165 | #else |
166 | H5Eset_autoH5Eset_auto2 (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_tH5E_auto2_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_181 |
188 | H5Eget_autoH5Eget_auto2 (H5E_DEFAULT0, &err_func, &err_func_data); |
189 | H5Eset_autoH5Eset_auto2 (H5E_DEFAULT0, 0, 0); |
190 | #else |
191 | H5Eget_autoH5Eget_auto2 (&err_func, &err_func_data); |
192 | H5Eset_autoH5Eset_auto2 (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_181 |
210 | H5Eset_autoH5Eset_auto2 (H5E_DEFAULT0, err_func, err_func_data); |
211 | #else |
212 | H5Eset_autoH5Eset_auto2 (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_181 |
280 | subgroup_id = H5GopenH5Gopen2 (group_id, name, H5P_DEFAULT0); |
281 | #else |
282 | subgroup_id = H5GopenH5Gopen2 (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_181 |
294 | data_id = H5DopenH5Dopen2 (subgroup_id, "type", H5P_DEFAULT0); |
295 | #else |
296 | data_id = H5DopenH5Dopen2 (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)octave_local_buffer<char> _buffer_typ (slen); char *typ = _buffer_typ; |
323 | |
324 | // create datatype for (null-terminated) string to read into: |
325 | hid_t st_id = H5Tcopy (H5T_C_S1(H5open(), H5T_C_S1_g)); |
326 | H5Tset_size (st_id, slen); |
327 | |
328 | if (H5Dread (data_id, st_id, H5S_ALL0, H5S_ALL0, H5P_DEFAULT0, |
329 | typ) < 0) |
330 | goto done; |
331 | |
332 | H5Tclose (st_id); |
333 | H5Dclose (data_id); |
334 | |
335 | d->tc = octave_value_typeinfo::lookup_type (typ); |
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")) |
351 | d->tc = octave_value_typeinfo::lookup_type ("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_181 |
368 | data_id = H5DopenH5Dopen2 (group_id, name, H5P_DEFAULT0); |
369 | #else |
370 | data_id = H5DopenH5Dopen2 (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((-127 -1)?(H5open(), H5T_NATIVE_SCHAR_g):(H5open(), H5T_NATIVE_UCHAR_g )): |
407 | int_typ = "int8 "; |
408 | break; |
409 | |
410 | case H5T_NATIVE_SHORT(H5open(), H5T_NATIVE_SHORT_g): |
411 | int_typ = "int16 "; |
412 | break; |
413 | |
414 | case H5T_NATIVE_INT(H5open(), H5T_NATIVE_INT_g): |
415 | case H5T_NATIVE_LONG(H5open(), H5T_NATIVE_LONG_g): |
416 | int_typ = "int32 "; |
417 | break; |
418 | |
419 | case H5T_NATIVE_LLONG(H5open(), H5T_NATIVE_LLONG_g): |
420 | int_typ = "int64 "; |
421 | break; |
422 | |
423 | case H5T_NATIVE_UCHAR(H5open(), H5T_NATIVE_UCHAR_g): |
424 | int_typ = "uint8 "; |
425 | break; |
426 | |
427 | case H5T_NATIVE_USHORT(H5open(), H5T_NATIVE_USHORT_g): |
428 | int_typ = "uint16 "; |
429 | break; |
430 | |
431 | case H5T_NATIVE_UINT(H5open(), H5T_NATIVE_UINT_g): |
432 | case H5T_NATIVE_ULONG(H5open(), H5T_NATIVE_ULONG_g): |
433 | int_typ = "uint32 "; |
434 | break; |
435 | |
436 | case H5T_NATIVE_ULLONG(H5open(), H5T_NATIVE_ULLONG_g): |
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(H5open(), H5T_NATIVE_DOUBLE_g)); |
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 |
Value stored to 'retval' is never read | |
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)octave_local_buffer<char> _buffer_tdoc (comment_length) ; char *tdoc = _buffer_tdoc; |
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); |
594 | hdf5_callback_data d; |
595 | |
596 | herr_t H5Giterate_retval = -1; |
597 | |
598 | hsize_t num_obj = 0; |
599 | #if HAVE_HDF5_181 |
600 | hid_t group_id = H5GopenH5Gopen2 (hs.file_id, "/", H5P_DEFAULT0); |
601 | #else |
602 | hid_t group_id = H5GopenH5Gopen2 (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_181 |
672 | hid_t a_id = H5AcreateH5Acreate2 (loc_id, attr_name, H5T_NATIVE_UCHAR(H5open(), H5T_NATIVE_UCHAR_g), |
673 | as_id, H5P_DEFAULT0, H5P_DEFAULT0); |
674 | #else |
675 | hid_t a_id = H5AcreateH5Acreate2 (loc_id, attr_name, |
676 | H5T_NATIVE_UCHAR(H5open(), H5T_NATIVE_UCHAR_g), as_id, H5P_DEFAULT0); |
677 | #endif |
678 | if (a_id >= 0) |
679 | { |
680 | unsigned char attr_val = 1; |
681 | |
682 | retval = H5Awrite (a_id, H5T_NATIVE_UCHAR(H5open(), H5T_NATIVE_UCHAR_g), &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_181 |
708 | hid_t a_id = H5AcreateH5Acreate2 (loc_id, attr_name, type_id, |
709 | as_id, H5P_DEFAULT0, H5P_DEFAULT0); |
710 | #else |
711 | hid_t a_id = H5AcreateH5Acreate2 (loc_id, attr_name, |
712 | type_id, as_id, H5P_DEFAULT0); |
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 (); |
739 | OCTAVE_LOCAL_BUFFER (octave_idx_type, dims, sz)octave_local_buffer<octave_idx_type> _buffer_dims (sz); octave_idx_type *dims = _buffer_dims; |
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_181 |
756 | data_hid = H5DcreateH5Dcreate2 (loc_id, name, H5T_NATIVE_IDX(H5open(), H5T_NATIVE_INT_g), space_hid, |
757 | H5P_DEFAULT0, H5P_DEFAULT0, H5P_DEFAULT0); |
758 | #else |
759 | data_hid = H5DcreateH5Dcreate2 (loc_id, name, H5T_NATIVE_IDX(H5open(), H5T_NATIVE_INT_g), space_hid, |
760 | H5P_DEFAULT0); |
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(H5open(), H5T_NATIVE_INT_g), H5S_ALL0, H5S_ALL0, |
769 | H5P_DEFAULT0, 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_181 |
792 | hid_t data_hid = H5DopenH5Dopen2 (loc_id, name, H5P_DEFAULT0); |
793 | #else |
794 | hid_t data_hid = H5DopenH5Dopen2 (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)octave_local_buffer<octave_idx_type> _buffer_dims (hdims ); octave_idx_type *dims = _buffer_dims; |
801 | |
802 | retval = H5Dread (data_hid, H5T_NATIVE_IDX(H5open(), H5T_NATIVE_INT_g), H5S_ALL0, H5S_ALL0, |
803 | H5P_DEFAULT0, 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_CONVERSIONS0 |
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(H5open(), H5T_NATIVE_UCHAR_g); |
831 | |
832 | case LS_U_SHORT: |
833 | return H5T_NATIVE_USHORT(H5open(), H5T_NATIVE_USHORT_g); |
834 | |
835 | case LS_U_INT: |
836 | return H5T_NATIVE_UINT(H5open(), H5T_NATIVE_UINT_g); |
837 | |
838 | case LS_CHAR: |
839 | return H5T_NATIVE_CHAR((-127 -1)?(H5open(), H5T_NATIVE_SCHAR_g):(H5open(), H5T_NATIVE_UCHAR_g )); |
840 | |
841 | case LS_SHORT: |
842 | return H5T_NATIVE_SHORT(H5open(), H5T_NATIVE_SHORT_g); |
843 | |
844 | case LS_INT: |
845 | return H5T_NATIVE_INT(H5open(), H5T_NATIVE_INT_g); |
846 | |
847 | case LS_FLOAT: |
848 | return H5T_NATIVE_FLOAT(H5open(), H5T_NATIVE_FLOAT_g); |
849 | |
850 | case LS_DOUBLE: |
851 | default: |
852 | return H5T_NATIVE_DOUBLE(H5open(), H5T_NATIVE_DOUBLE_g); |
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 () |
875 | || val.type_id () == octave_lazy_index::static_type_id ()) |
876 | val = val.full_value (); |
877 | |
878 | std::string t = val.type_name (); |
879 | #if HAVE_HDF5_181 |
880 | data_id = H5GcreateH5Gcreate2 (loc_id, name.c_str (), H5P_DEFAULT0, H5P_DEFAULT0, |
881 | H5P_DEFAULT0); |
882 | #else |
883 | data_id = H5GcreateH5Gcreate2 (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(H5open(), H5T_C_S1_g)); 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_181 |
898 | data_type_id = H5DcreateH5Dcreate2 (data_id, "type", type_id, space_id, |
899 | H5P_DEFAULT0, H5P_DEFAULT0, H5P_DEFAULT0); |
900 | #else |
901 | data_type_id = H5DcreateH5Dcreate2 (data_id, "type", type_id, space_id, H5P_DEFAULT0); |
902 | #endif |
903 | if (data_type_id < 0 || H5Dwrite (data_type_id, type_id, H5S_ALL0, H5S_ALL0, |
904 | H5P_DEFAULT0, 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 |