1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1996-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29
30 #if defined (HAVE_HDF5)
31
32 #include <cctype>
33
34 #include <iomanip>
35 #include <istream>
36 #include <limits>
37 #include <ostream>
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 "errwarn.h"
57 #include "interpreter.h"
58 #include "interpreter-private.h"
59 #include "load-save.h"
60 #include "oct-hdf5.h"
61 #include "ovl.h"
62 #include "oct-map.h"
63 #include "ov-cell.h"
64 #include "pager.h"
65 #include "sysdep.h"
66 #include "unwind-prot.h"
67 #include "utils.h"
68 #include "variables.h"
69 #include "version.h"
70 #include "dMatrix.h"
71 #include "ov-lazy-idx.h"
72
73 #include "ls-utils.h"
74 #include "ls-hdf5.h"
75
76 #if defined (HAVE_HDF5)
77
78 static hid_t
check_hdf5_id_value(octave_hdf5_id id,const char * who)79 check_hdf5_id_value (octave_hdf5_id id, const char *who)
80 {
81 if (id > std::numeric_limits<hid_t>::max ())
82 error ("%s: internal error: ID too large for hid_t", who);
83
84 return static_cast<hid_t> (id);
85 }
86
87 #endif
88
hdf5_fstreambase(const char * name,int mode,int)89 hdf5_fstreambase::hdf5_fstreambase (const char *name, int mode, int /* prot */)
90 : file_id (-1), current_item (-1)
91 {
92 #if defined (HAVE_HDF5)
93 open_create (name, mode);
94
95 current_item = 0;
96
97 #else
98 err_disabled_feature ("hdf5_fstreambase", "HDF5");
99 #endif
100 }
101
102 void
close(void)103 hdf5_fstreambase::close (void)
104 {
105 #if defined (HAVE_HDF5)
106
107 if (file_id >= 0)
108 {
109 if (H5Fclose (file_id) < 0)
110 std::ios::setstate (std::ios::badbit);
111 file_id = -1;
112 }
113
114 #else
115 // This shouldn't happen because construction of hdf5_fstreambase
116 // objects is supposed to be impossible if HDF5 is not available.
117
118 panic_impossible ();
119 #endif
120 }
121
122 void
open(const char * name,int mode,int)123 hdf5_fstreambase::open (const char *name, int mode, int)
124 {
125 #if defined (HAVE_HDF5)
126
127 clear ();
128
129 open_create (name, mode);
130
131 current_item = 0;
132
133 #else
134 // This shouldn't happen because construction of hdf5_fstreambase
135 // objects is supposed to be impossible if HDF5 is not available.
136
137 panic_impossible ();
138 #endif
139 }
140
141 void
open_create(const char * name,int mode)142 hdf5_fstreambase::open_create (const char *name, int mode)
143 {
144 #if defined (HAVE_HDF5)
145 // Open the HDF5 file NAME. If it does not exist, create the file.
146
147 std::string fname_str (name);
148 std::string ascii_fname_str = octave::sys::get_ASCII_filename (fname_str);
149 const char *ascii_fname = ascii_fname_str.c_str ();
150
151 if (mode & std::ios::in)
152 file_id = H5Fopen (ascii_fname, H5F_ACC_RDONLY, octave_H5P_DEFAULT);
153 else if (mode & std::ios::out)
154 {
155 if (mode & std::ios::app && H5Fis_hdf5 (ascii_fname) > 0)
156 file_id = H5Fopen (ascii_fname, H5F_ACC_RDWR, octave_H5P_DEFAULT);
157 else
158 # if defined (OCTAVE_USE_WINDOWS_API)
159 {
160 // Check whether file already exists
161 std::string abs_ascii_fname
162 = octave::sys::canonicalize_file_name (ascii_fname_str);
163 if (! abs_ascii_fname.empty ())
164 {
165 // Use the existing file
166 file_id = H5Fcreate (ascii_fname, H5F_ACC_TRUNC,
167 octave_H5P_DEFAULT, octave_H5P_DEFAULT);
168 if (file_id < 0)
169 std::ios::setstate (std::ios::badbit);
170
171 return;
172 }
173
174 // Check whether filename contains non-ASCII (UTF-8) characters.
175 std::string::const_iterator first_non_ASCII
176 = std::find_if (fname_str.begin (), fname_str.end (),
177 [](char c) { return (c < 0 || c >= 128); });
178 if (first_non_ASCII == fname_str.end ())
179 {
180 // No non-ASCII characters
181 file_id = H5Fcreate (name, H5F_ACC_TRUNC, octave_H5P_DEFAULT,
182 octave_H5P_DEFAULT);
183 if (file_id < 0)
184 std::ios::setstate (std::ios::badbit);
185
186 return;
187 }
188
189 // Create file in temp folder
190 std::string tmp_name = octave::sys::tempnam ("", "oct-");
191 octave_hdf5_id hdf5_fid = H5Fcreate (tmp_name.c_str (), H5F_ACC_TRUNC,
192 octave_H5P_DEFAULT,
193 octave_H5P_DEFAULT);
194 if (hdf5_fid < 0)
195 {
196 file_id = -1;
197 std::ios::setstate (std::ios::badbit);
198 return;
199 }
200
201 // Close file
202 H5Fclose (hdf5_fid);
203
204 // Move temporary file to final destination
205 std::string msg;
206 int res = octave::sys::rename (tmp_name, name, msg);
207 if (res < 0)
208 {
209 std::ios::setstate (std::ios::badbit);
210 file_id = -1;
211 return;
212 }
213
214 // Open file at final location
215 ascii_fname_str = octave::sys::get_ASCII_filename (fname_str);
216 ascii_fname = ascii_fname_str.c_str ();
217 file_id = H5Fopen (ascii_fname, H5F_ACC_RDWR, octave_H5P_DEFAULT);
218 }
219 # else
220 file_id = H5Fcreate (name, H5F_ACC_TRUNC, octave_H5P_DEFAULT,
221 octave_H5P_DEFAULT);
222 # endif
223 }
224 if (file_id < 0)
225 std::ios::setstate (std::ios::badbit);
226
227 return;
228
229 #else
230 // This shouldn't happen because construction of hdf5_fstreambase
231 // objects is supposed to be impossible if HDF5 is not available.
232
233 panic_impossible ();
234 #endif
235 }
236
237 static std::string
make_valid_identifier(const std::string & nm)238 make_valid_identifier (const std::string& nm)
239 {
240 std::string retval;
241
242 std::size_t nm_len = nm.length ();
243
244 if (nm_len > 0)
245 {
246 if (! isalpha (nm[0]))
247 retval += '_';
248
249 for (std::size_t i = 0; i < nm_len; i++)
250 {
251 char c = nm[i];
252 retval += (isalnum (c) || c == '_') ? c : '_';
253 }
254 }
255
256 return retval;
257 }
258
259 // Given two compound types t1 and t2, determine whether they
260 // are compatible for reading/writing. This function only
261 // works for non-nested types composed of simple elements (ints, floats...),
262 // which is all we need it for
263
264 bool
hdf5_types_compatible(octave_hdf5_id t1,octave_hdf5_id t2)265 hdf5_types_compatible (octave_hdf5_id t1, octave_hdf5_id t2)
266 {
267 #if defined (HAVE_HDF5)
268
269 int n;
270 if ((n = H5Tget_nmembers (t1)) != H5Tget_nmembers (t2))
271 return false;
272
273 for (int i = 0; i < n; ++i)
274 {
275 hid_t mt1 = H5Tget_member_type (t1, i);
276 hid_t mt2 = H5Tget_member_type (t2, i);
277
278 if (H5Tget_class (mt1) != H5Tget_class (mt2))
279 return false;
280
281 H5Tclose (mt2);
282 H5Tclose (mt1);
283 }
284
285 return true;
286
287 #else
288 err_disabled_feature ("hdf5_types_compatible", "HDF5");
289 #endif
290 }
291
292 // Return true if loc_id has the attribute named attr_name, and false
293 // otherwise.
294
295 bool
hdf5_check_attr(octave_hdf5_id loc_id,const char * attr_name)296 hdf5_check_attr (octave_hdf5_id loc_id, const char *attr_name)
297 {
298 #if defined (HAVE_HDF5)
299
300 bool retval = false;
301
302 // we have to pull some shenanigans here to make sure
303 // HDF5 doesn't print out all sorts of error messages if we
304 // call H5Aopen for a non-existing attribute
305
306 H5E_auto_t err_func;
307 void *err_func_data;
308
309 // turn off error reporting temporarily, but save the error
310 // reporting function:
311
312 #if defined (HAVE_HDF5_18)
313 H5Eget_auto (octave_H5E_DEFAULT, &err_func, &err_func_data);
314 H5Eset_auto (octave_H5E_DEFAULT, nullptr, nullptr);
315 #else
316 H5Eget_auto (&err_func, &err_func_data);
317 H5Eset_auto (nullptr, nullptr);
318 #endif
319
320 hid_t attr_id = H5Aopen_name (loc_id, attr_name);
321
322 if (attr_id >= 0)
323 {
324 // successful
325 retval = true;
326 H5Aclose (attr_id);
327 }
328
329 // restore error reporting:
330 #if defined (HAVE_HDF5_18)
331 H5Eset_auto (octave_H5E_DEFAULT, err_func, err_func_data);
332 #else
333 H5Eset_auto (err_func, err_func_data);
334 #endif
335 return retval;
336
337 #else
338 err_disabled_feature ("hdf5_check_attr", "HDF5");
339 #endif
340 }
341
342 bool
hdf5_get_scalar_attr(octave_hdf5_id loc_id,octave_hdf5_id type_id,const char * attr_name,void * buf)343 hdf5_get_scalar_attr (octave_hdf5_id loc_id, octave_hdf5_id type_id,
344 const char *attr_name, void *buf)
345 {
346 #if defined (HAVE_HDF5)
347
348 bool retval = false;
349
350 // we have to pull some shenanigans here to make sure
351 // HDF5 doesn't print out all sorts of error messages if we
352 // call H5Aopen for a non-existing attribute
353
354 H5E_auto_t err_func;
355 void *err_func_data;
356
357 // turn off error reporting temporarily, but save the error
358 // reporting function:
359
360 #if defined (HAVE_HDF5_18)
361 H5Eget_auto (octave_H5E_DEFAULT, &err_func, &err_func_data);
362 H5Eset_auto (octave_H5E_DEFAULT, nullptr, nullptr);
363 #else
364 H5Eget_auto (&err_func, &err_func_data);
365 H5Eset_auto (nullptr, nullptr);
366 #endif
367
368 hid_t attr_id = H5Aopen_name (loc_id, attr_name);
369
370 if (attr_id >= 0)
371 {
372 hid_t space_id = H5Aget_space (attr_id);
373
374 hsize_t rank = H5Sget_simple_extent_ndims (space_id);
375
376 if (rank == 0)
377 retval = H5Aread (attr_id, type_id, buf) >= 0;
378 H5Aclose (attr_id);
379 }
380
381 // restore error reporting:
382 #if defined (HAVE_HDF5_18)
383 H5Eset_auto (octave_H5E_DEFAULT, err_func, err_func_data);
384 #else
385 H5Eset_auto (err_func, err_func_data);
386 #endif
387 return retval;
388
389 #else
390 err_disabled_feature ("hdf5_get_scalar_attr", "HDF5");
391 #endif
392 }
393
394 // The following subroutines creates an HDF5 representations of the way
395 // we will store Octave complex types (pairs of floating-point numbers).
396 // NUM_TYPE is the HDF5 numeric type to use for storage (e.g.
397 // H5T_NATIVE_DOUBLE to save as 'double'). Note that any necessary
398 // conversions are handled automatically by HDF5.
399
400 octave_hdf5_id
hdf5_make_complex_type(octave_hdf5_id num_type)401 hdf5_make_complex_type (octave_hdf5_id num_type)
402 {
403 #if defined (HAVE_HDF5)
404
405 hid_t type_id = H5Tcreate (H5T_COMPOUND, sizeof (double) * 2);
406
407 H5Tinsert (type_id, "real", 0 * sizeof (double), num_type);
408 H5Tinsert (type_id, "imag", 1 * sizeof (double), num_type);
409
410 return type_id;
411
412 #else
413 err_disabled_feature ("hdf5_make_complex_type", "HDF5");
414 #endif
415 }
416
417 #if defined (HAVE_HDF5)
418
419 // The following subroutine creates an HDF5 representation of the way
420 // we will store Octave range types (triplets of floating-point numbers).
421 // NUM_TYPE is the HDF5 numeric type to use for storage
422 // (e.g., H5T_NATIVE_DOUBLE to save as 'double').
423 // Note that any necessary conversions are handled automatically by HDF5.
424
425 static hid_t
hdf5_make_range_type(hid_t num_type)426 hdf5_make_range_type (hid_t num_type)
427 {
428 hid_t type_id = H5Tcreate (H5T_COMPOUND, sizeof (double) * 3);
429
430 H5Tinsert (type_id, "base", 0 * sizeof (double), num_type);
431 H5Tinsert (type_id, "limit", 1 * sizeof (double), num_type);
432 H5Tinsert (type_id, "increment", 2 * sizeof (double), num_type);
433
434 return type_id;
435 }
436
437 static herr_t
load_inline_fcn(hid_t loc_id,const char * name,octave_value & retval)438 load_inline_fcn (hid_t loc_id, const char *name, octave_value& retval)
439 {
440 #if defined (HAVE_HDF5)
441
442 hid_t group_hid, data_hid, space_hid, type_hid, type_class_hid, st_id;
443 hsize_t rank;
444 int slen;
445
446 #if defined (HAVE_HDF5_18)
447 group_hid = H5Gopen (loc_id, name, octave_H5P_DEFAULT);
448 #else
449 group_hid = H5Gopen (loc_id, name);
450 #endif
451 if (group_hid < 0) return -1;
452
453 #if defined (HAVE_HDF5_18)
454 data_hid = H5Dopen (group_hid, "args", octave_H5P_DEFAULT);
455 #else
456 data_hid = H5Dopen (group_hid, "args");
457 #endif
458 space_hid = H5Dget_space (data_hid);
459 rank = H5Sget_simple_extent_ndims (space_hid);
460
461 if (rank != 2)
462 {
463 H5Dclose (data_hid);
464 H5Sclose (space_hid);
465 H5Gclose (group_hid);
466 return -1;
467 }
468
469 OCTAVE_LOCAL_BUFFER (hsize_t, hdims, rank);
470 OCTAVE_LOCAL_BUFFER (hsize_t, maxdims, rank);
471
472 H5Sget_simple_extent_dims (space_hid, hdims, maxdims);
473
474 octave_value_list args (hdims[1]+1);
475
476 OCTAVE_LOCAL_BUFFER (char, s1, hdims[0] * hdims[1]);
477
478 if (H5Dread (data_hid, H5T_NATIVE_UCHAR, octave_H5S_ALL, octave_H5S_ALL,
479 octave_H5P_DEFAULT, s1) < 0)
480 {
481 H5Dclose (data_hid);
482 H5Sclose (space_hid);
483 H5Gclose (group_hid);
484 return -1;
485 }
486
487 H5Dclose (data_hid);
488 H5Sclose (space_hid);
489
490 for (std::size_t i = 0; i < hdims[1]; i++)
491 args(i+1) = std::string (s1 + i*hdims[0]);
492
493 #if defined (HAVE_HDF5_18)
494 data_hid = H5Dopen (group_hid, "nm", octave_H5P_DEFAULT);
495 #else
496 data_hid = H5Dopen (group_hid, "nm");
497 #endif
498
499 if (data_hid < 0)
500 {
501 H5Gclose (group_hid);
502 return -1;
503 }
504
505 type_hid = H5Dget_type (data_hid);
506 type_class_hid = H5Tget_class (type_hid);
507
508 if (type_class_hid != H5T_STRING)
509 {
510 H5Tclose (type_hid);
511 H5Dclose (data_hid);
512 H5Gclose (group_hid);
513 return -1;
514 }
515
516 space_hid = H5Dget_space (data_hid);
517 rank = H5Sget_simple_extent_ndims (space_hid);
518
519 if (rank != 0)
520 {
521 H5Sclose (space_hid);
522 H5Tclose (type_hid);
523 H5Dclose (data_hid);
524 H5Gclose (group_hid);
525 return -1;
526 }
527
528 slen = H5Tget_size (type_hid);
529 if (slen < 0)
530 {
531 H5Sclose (space_hid);
532 H5Tclose (type_hid);
533 H5Dclose (data_hid);
534 H5Gclose (group_hid);
535 return -1;
536 }
537
538 OCTAVE_LOCAL_BUFFER (char, nm_tmp, slen);
539
540 // create datatype for (null-terminated) string to read into:
541 st_id = H5Tcopy (H5T_C_S1);
542 H5Tset_size (st_id, slen);
543
544 if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL,
545 octave_H5P_DEFAULT, nm_tmp) < 0)
546 {
547 H5Sclose (space_hid);
548 H5Tclose (type_hid);
549 H5Gclose (group_hid);
550 return -1;
551 }
552 H5Tclose (st_id);
553 H5Dclose (data_hid);
554 // NAME is obsolete and unused.
555 // std::string name (nm_tmp);
556
557 #if defined (HAVE_HDF5_18)
558 data_hid = H5Dopen (group_hid, "iftext", octave_H5P_DEFAULT);
559 #else
560 data_hid = H5Dopen (group_hid, "iftext");
561 #endif
562
563 if (data_hid < 0)
564 {
565 H5Gclose (group_hid);
566 return -1;
567 }
568
569 type_hid = H5Dget_type (data_hid);
570 type_class_hid = H5Tget_class (type_hid);
571
572 if (type_class_hid != H5T_STRING)
573 {
574 H5Tclose (type_hid);
575 H5Dclose (data_hid);
576 H5Gclose (group_hid);
577 return -1;
578 }
579
580 space_hid = H5Dget_space (data_hid);
581 rank = H5Sget_simple_extent_ndims (space_hid);
582
583 if (rank != 0)
584 {
585 H5Sclose (space_hid);
586 H5Tclose (type_hid);
587 H5Dclose (data_hid);
588 H5Gclose (group_hid);
589 return -1;
590 }
591
592 slen = H5Tget_size (type_hid);
593 if (slen < 0)
594 {
595 H5Sclose (space_hid);
596 H5Tclose (type_hid);
597 H5Dclose (data_hid);
598 H5Gclose (group_hid);
599 return -1;
600 }
601
602 OCTAVE_LOCAL_BUFFER (char, iftext_tmp, slen);
603
604 // create datatype for (null-terminated) string to read into:
605 st_id = H5Tcopy (H5T_C_S1);
606 H5Tset_size (st_id, slen);
607
608 if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL,
609 octave_H5P_DEFAULT, iftext_tmp) < 0)
610 {
611 H5Sclose (space_hid);
612 H5Tclose (type_hid);
613 H5Gclose (group_hid);
614 return -1;
615 }
616 H5Tclose (st_id);
617 H5Dclose (data_hid);
618
619 args(0) = std::string (iftext_tmp);
620
621 octave::interpreter& interp
622 = octave::__get_interpreter__ ("load_inline_fcn");
623
624 octave_value_list tmp = interp.feval ("inline", args, 1);
625
626 if (tmp.length () > 0)
627 {
628 retval = tmp(0);
629 return 1;
630 }
631
632 #else
633 octave_unused_parameter (loc_id);
634 octave_unused_parameter (name);
635 octave_unused_parameter (retval);
636
637 warn_load ("hdf5");
638 #endif
639
640 return -1;
641 }
642
643 // This function is designed to be passed to H5Giterate, which calls it
644 // on each data item in an HDF5 file. For the item whose name is NAME in
645 // the group GROUP_ID, this function sets dv->tc to an Octave representation
646 // of that item. (dv must be a pointer to hdf5_callback_data.) (It also
647 // sets the other fields of dv).
648 //
649 // It returns 1 on success (in which case H5Giterate stops and returns),
650 // -1 on error, and 0 to tell H5Giterate to continue on to the next item
651 // (e.g., if NAME was a data type we don't recognize).
652 //
653 // This function must not throw an exception.
654
655 static herr_t
hdf5_read_next_data_internal(hid_t group_id,const char * name,void * dv)656 hdf5_read_next_data_internal (hid_t group_id, const char *name, void *dv)
657 {
658 hdf5_callback_data *d = static_cast<hdf5_callback_data *> (dv);
659 hid_t type_id = -1;
660 hid_t type_class_id = -1;
661 hid_t data_id = -1;
662 hid_t subgroup_id = -1;
663 hid_t space_id = -1;;
664
665 H5G_stat_t info;
666 herr_t retval = 0;
667 bool ident_valid = octave::valid_identifier (name);
668
669 std::string vname = name;
670
671 octave::type_info& type_info
672 = octave::__get_type_info__ ("hdf5_read_next_data_internal");
673
674 // Allow identifiers as all digits so we can load lists saved by
675 // earlier versions of Octave.
676
677 if (! ident_valid)
678 {
679 // fix the identifier, replacing invalid chars with underscores
680 vname = make_valid_identifier (vname);
681
682 // check again (in case vname was null, empty, or some such thing):
683 ident_valid = octave::valid_identifier (vname);
684 }
685
686 H5Gget_objinfo (group_id, name, 1, &info);
687
688 if (info.type == H5G_GROUP && ident_valid)
689 {
690 #if defined (HAVE_HDF5_18)
691 subgroup_id = H5Gopen (group_id, name, octave_H5P_DEFAULT);
692 #else
693 subgroup_id = H5Gopen (group_id, name);
694 #endif
695
696 if (subgroup_id < 0)
697 {
698 retval = subgroup_id;
699 goto done;
700 }
701
702 if (hdf5_check_attr (subgroup_id, "OCTAVE_NEW_FORMAT"))
703 {
704 #if defined (HAVE_HDF5_18)
705 data_id = H5Dopen (subgroup_id, "type", octave_H5P_DEFAULT);
706 #else
707 data_id = H5Dopen (subgroup_id, "type");
708 #endif
709
710 if (data_id < 0)
711 {
712 retval = data_id;
713 goto done;
714 }
715
716 type_id = H5Dget_type (data_id);
717
718 type_class_id = H5Tget_class (type_id);
719
720 if (type_class_id != H5T_STRING)
721 goto done;
722
723 space_id = H5Dget_space (data_id);
724 hsize_t rank = H5Sget_simple_extent_ndims (space_id);
725
726 if (rank != 0)
727 goto done;
728
729 int slen = H5Tget_size (type_id);
730 if (slen < 0)
731 goto done;
732
733 OCTAVE_LOCAL_BUFFER (char, typ, slen);
734
735 // create datatype for (null-terminated) string to read into:
736 hid_t st_id = H5Tcopy (H5T_C_S1);
737 H5Tset_size (st_id, slen);
738
739 if (H5Dread (data_id, st_id, octave_H5S_ALL, octave_H5S_ALL,
740 octave_H5P_DEFAULT, typ) < 0)
741 goto done;
742
743 H5Tclose (st_id);
744 H5Dclose (data_id);
745
746 if (std::string (typ, slen-1) == "inline function")
747 {
748 retval = load_inline_fcn (subgroup_id, name, d->tc);
749 }
750 else
751 {
752 d->tc = type_info.lookup_type (std::string (typ, slen-1));
753
754 try
755 {
756 retval = (d->tc.load_hdf5 (subgroup_id, "value") ? 1 : -1);
757 }
758 catch (const octave::execution_exception& ee)
759 {
760 retval = -1;
761 }
762 }
763
764 // check for OCTAVE_GLOBAL attribute:
765 d->global = hdf5_check_attr (subgroup_id, "OCTAVE_GLOBAL");
766
767 H5Gclose (subgroup_id);
768 }
769 else
770 {
771 // It seems that this block only applies to an old list type
772 // and that we shouldn't need to handle the old inline
773 // function type here.
774
775 // an HDF5 group is treated as an octave structure by
776 // default (since that preserves name information), and an
777 // octave list otherwise.
778
779 if (hdf5_check_attr (subgroup_id, "OCTAVE_LIST"))
780 d->tc = type_info.lookup_type ("list");
781 else
782 d->tc = type_info.lookup_type ("struct");
783
784 // check for OCTAVE_GLOBAL attribute:
785 d->global = hdf5_check_attr (subgroup_id, "OCTAVE_GLOBAL");
786
787 H5Gclose (subgroup_id);
788
789 try
790 {
791 retval = (d->tc.load_hdf5 (group_id, name) ? 1 : -1);
792 }
793 catch (const octave::execution_exception& ee)
794 {
795 retval = -1;
796 }
797 }
798
799 }
800 else if (info.type == H5G_DATASET && ident_valid)
801 {
802 // It seems that this block only applies to an old version of
803 // Octave HDF5 files and that it is probably not important to
804 // handle the old inline function type here.
805
806 // For backwards compatibility.
807 #if defined (HAVE_HDF5_18)
808 data_id = H5Dopen (group_id, name, octave_H5P_DEFAULT);
809 #else
810 data_id = H5Dopen (group_id, name);
811 #endif
812
813 if (data_id < 0)
814 {
815 retval = data_id;
816 goto done;
817 }
818
819 type_id = H5Dget_type (data_id);
820
821 type_class_id = H5Tget_class (type_id);
822
823 if (type_class_id == H5T_FLOAT)
824 {
825 space_id = H5Dget_space (data_id);
826
827 hsize_t rank = H5Sget_simple_extent_ndims (space_id);
828
829 if (rank == 0)
830 d->tc = type_info.lookup_type ("scalar");
831 else
832 d->tc = type_info.lookup_type ("matrix");
833
834 H5Sclose (space_id);
835 }
836 else if (type_class_id == H5T_INTEGER)
837 {
838 // What integer type do we really have..
839 std::string int_typ;
840 #if defined (HAVE_H5T_GET_NATIVE_TYPE)
841 // FIXME: test this code and activated with an autoconf
842 // test!! It is also incorrect for 64-bit indexing!!
843
844 switch (H5Tget_native_type (type_id, H5T_DIR_ASCEND))
845 {
846 case H5T_NATIVE_CHAR:
847 int_typ = "int8 ";
848 break;
849
850 case H5T_NATIVE_SHORT:
851 int_typ = "int16 ";
852 break;
853
854 case H5T_NATIVE_INT:
855 case H5T_NATIVE_LONG:
856 int_typ = "int32 ";
857 break;
858
859 case H5T_NATIVE_LLONG:
860 int_typ = "int64 ";
861 break;
862
863 case H5T_NATIVE_UCHAR:
864 int_typ = "uint8 ";
865 break;
866
867 case H5T_NATIVE_USHORT:
868 int_typ = "uint16 ";
869 break;
870
871 case H5T_NATIVE_UINT:
872 case H5T_NATIVE_ULONG:
873 int_typ = "uint32 ";
874 break;
875
876 case H5T_NATIVE_ULLONG:
877 int_typ = "uint64 ";
878 break;
879 }
880 #else
881 hid_t int_sign = H5Tget_sign (type_id);
882
883 if (int_sign == H5T_SGN_ERROR)
884 warning ("load: can't read '%s' (unknown datatype)", name);
885 else
886 {
887 if (int_sign == H5T_SGN_NONE)
888 int_typ.push_back ('u');
889 int_typ.append ("int");
890
891 int slen = H5Tget_size (type_id);
892 if (slen < 0)
893 warning ("load: can't read '%s' (unknown datatype)", name);
894 else
895 {
896 switch (slen)
897 {
898 case 1:
899 int_typ.append ("8 ");
900 break;
901
902 case 2:
903 int_typ.append ("16 ");
904 break;
905
906 case 4:
907 int_typ.append ("32 ");
908 break;
909
910 case 8:
911 int_typ.append ("64 ");
912 break;
913
914 default:
915 warning ("load: can't read '%s' (unknown datatype)",
916 name);
917 int_typ = "";
918 break;
919 }
920 }
921 }
922 #endif
923 if (int_typ == "")
924 warning ("load: can't read '%s' (unknown datatype)", name);
925 else
926 {
927 // Matrix or scalar?
928 space_id = H5Dget_space (data_id);
929
930 hsize_t rank = H5Sget_simple_extent_ndims (space_id);
931
932 if (rank == 0)
933 int_typ.append ("scalar");
934 else
935 int_typ.append ("matrix");
936
937 d->tc = type_info.lookup_type (int_typ);
938 H5Sclose (space_id);
939 }
940 }
941 else if (type_class_id == H5T_STRING)
942 d->tc = type_info.lookup_type ("string");
943 else if (type_class_id == H5T_COMPOUND)
944 {
945 hid_t complex_type = hdf5_make_complex_type (H5T_NATIVE_DOUBLE);
946 hid_t range_type = hdf5_make_range_type (H5T_NATIVE_DOUBLE);
947
948 if (hdf5_types_compatible (type_id, complex_type))
949 {
950 // read complex matrix or scalar variable
951 space_id = H5Dget_space (data_id);
952 hsize_t rank = H5Sget_simple_extent_ndims (space_id);
953
954 if (rank == 0)
955 d->tc = type_info.lookup_type ("complex scalar");
956 else
957 d->tc = type_info.lookup_type ("complex matrix");
958
959 H5Sclose (space_id);
960 }
961 else if (hdf5_types_compatible (type_id, range_type))
962 {
963 // If it's not a complex, check if it's a range
964 d->tc = octave_value_typeinfo::lookup_type ("range");
965 }
966 else // Otherwise, just ignore it with a warning.
967 {
968 warning ("load: can't read '%s' (unknown datatype)", name);
969 retval = 0; // unknown datatype; skip
970 return retval;
971 }
972
973 H5Tclose (range_type);
974 H5Tclose (complex_type);
975 }
976 else
977 {
978 warning ("load: can't read '%s' (unknown datatype)", name);
979 retval = 0; // unknown datatype; skip
980 return retval;
981 }
982
983 // check for OCTAVE_GLOBAL attribute:
984 d->global = hdf5_check_attr (data_id, "OCTAVE_GLOBAL");
985
986 try
987 {
988 retval = (d->tc.load_hdf5 (group_id, name) ? 1 : -1);
989 }
990 catch (const octave::execution_exception& ee)
991 {
992 retval = -1;
993 }
994
995 H5Tclose (type_id);
996 H5Dclose (data_id);
997 }
998
999 if (! ident_valid)
1000 {
1001 // should we attempt to handle invalid identifiers by converting
1002 // bad characters to '_', say?
1003 warning ("load: skipping invalid identifier '%s' in hdf5 file",
1004 name);
1005 }
1006
1007 done:
1008 if (retval < 0)
1009 {
1010 // Must be warning. A call to error aborts and leaves H5Giterate in
1011 // a mangled state that causes segfault on exit (bug #56149).
1012 warning ("load: error while reading hdf5 item '%s'", name);
1013 }
1014
1015 if (retval > 0)
1016 {
1017 // get documentation string, if any:
1018 int comment_length = H5Gget_comment (group_id, name, 0, nullptr);
1019
1020 if (comment_length > 1)
1021 {
1022 OCTAVE_LOCAL_BUFFER (char, tdoc, comment_length);
1023 H5Gget_comment (group_id, name, comment_length, tdoc);
1024 d->doc = tdoc;
1025 }
1026 else if (vname != name)
1027 {
1028 // the name was changed; store the original name
1029 // as the documentation string:
1030 d->doc = name;
1031 }
1032
1033 // copy name (actually, vname):
1034 d->name = vname;
1035 }
1036
1037 return retval;
1038 }
1039
1040 #endif
1041
1042 octave_hdf5_err
hdf5_read_next_data(octave_hdf5_id group_id,const char * name,void * dv)1043 hdf5_read_next_data (octave_hdf5_id group_id, const char *name, void *dv)
1044 {
1045 #if defined (HAVE_HDF5)
1046
1047 hid_t new_id = check_hdf5_id_value (group_id, "hdf5_read_next_data");
1048
1049 return hdf5_read_next_data_internal (new_id, name, dv);
1050
1051 #else
1052 err_disabled_feature ("hdf5_read_next_data", "HDF5");
1053 #endif
1054 }
1055
1056 octave_hdf5_err
hdf5_h5g_iterate(octave_hdf5_id loc_id,const char * name,int * idx,void * operator_data)1057 hdf5_h5g_iterate (octave_hdf5_id loc_id, const char *name, int *idx,
1058 void *operator_data)
1059 {
1060 #if defined (HAVE_HDF5)
1061
1062 hid_t new_id = check_hdf5_id_value (loc_id, "hdf5_h5g_iterate");
1063
1064 return H5Giterate (new_id, name, idx, hdf5_read_next_data_internal,
1065 operator_data);
1066
1067 #else
1068 err_disabled_feature ("hdf5_h5g_iterate", "HDF5");
1069 #endif
1070 }
1071
1072 // Read the next Octave variable from the stream IS, which must really be an
1073 // hdf5_ifstream. Return the variable value in tc, its docstring in doc, and
1074 // whether it is global in global. The return value is the name of the
1075 // variable, or NULL if none were found or there was an error.
1076 std::string
read_hdf5_data(std::istream & is,const std::string &,bool & global,octave_value & tc,std::string & doc,const string_vector & argv,int argv_idx,int argc)1077 read_hdf5_data (std::istream& is, const std::string& /* filename */,
1078 bool& global, octave_value& tc, std::string& doc,
1079 const string_vector& argv, int argv_idx, int argc)
1080 {
1081 #if defined (HAVE_HDF5)
1082
1083 check_hdf5_types ();
1084
1085 std::string retval;
1086
1087 doc.clear ();
1088
1089 hdf5_ifstream& hs = dynamic_cast<hdf5_ifstream&> (is);
1090 hdf5_callback_data d;
1091
1092 herr_t H5Giterate_retval = -1;
1093
1094 hsize_t num_obj = 0;
1095 #if defined (HAVE_HDF5_18)
1096 hid_t group_id = H5Gopen (hs.file_id, "/", octave_H5P_DEFAULT);
1097 #else
1098 hid_t group_id = H5Gopen (hs.file_id, "/");
1099 #endif
1100 H5Gget_num_objs (group_id, &num_obj);
1101 H5Gclose (group_id);
1102
1103 // For large datasets and out-of-core functionality,
1104 // check if only parts of the data is requested
1105 bool load_named_vars = argv_idx < argc;
1106 while (load_named_vars && hs.current_item < static_cast<int> (num_obj))
1107 {
1108 std::vector<char> var_name;
1109 bool found = false;
1110 std::size_t len = 0;
1111
1112 len = H5Gget_objname_by_idx (hs.file_id, hs.current_item, nullptr, 0);
1113 var_name.resize (len+1);
1114 H5Gget_objname_by_idx (hs.file_id, hs.current_item, &var_name[0], len+1);
1115
1116 for (int i = argv_idx; i < argc; i++)
1117 {
1118 glob_match pattern (argv[i]);
1119 if (pattern.match (std::string (&var_name[0])))
1120 {
1121 found = true;
1122 break;
1123 }
1124 }
1125
1126 if (found)
1127 break;
1128
1129 hs.current_item++;
1130 }
1131
1132 if (hs.current_item < static_cast<int> (num_obj))
1133 H5Giterate_retval = H5Giterate (hs.file_id, "/", &hs.current_item,
1134 hdf5_read_next_data_internal, &d);
1135
1136 if (H5Giterate_retval > 0)
1137 {
1138 global = d.global;
1139 tc = d.tc;
1140 doc = d.doc;
1141 }
1142 else
1143 {
1144 // An error occurred (H5Giterate_retval < 0),
1145 // or there are no more datasets (H5Giterate_retval == 0).
1146 // hdf5_read_next_data_internal has already printed a warning msg.
1147 }
1148
1149 if (! d.name.empty ())
1150 retval = d.name;
1151
1152 return retval;
1153
1154 #else
1155 err_disabled_feature ("read_hdf5_data", "HDF5");
1156 #endif
1157 }
1158
1159 // Add an attribute named attr_name to loc_id (a simple scalar
1160 // attribute with value 1). Return value is >= 0 on success.
1161 octave_hdf5_err
hdf5_add_attr(octave_hdf5_id loc_id,const char * attr_name)1162 hdf5_add_attr (octave_hdf5_id loc_id, const char *attr_name)
1163 {
1164 #if defined (HAVE_HDF5)
1165
1166 herr_t retval = 0;
1167
1168 hid_t as_id = H5Screate (H5S_SCALAR);
1169
1170 if (as_id >= 0)
1171 {
1172 #if defined (HAVE_HDF5_18)
1173 hid_t a_id = H5Acreate (loc_id, attr_name, H5T_NATIVE_UCHAR,
1174 as_id, octave_H5P_DEFAULT, octave_H5P_DEFAULT);
1175 #else
1176 hid_t a_id = H5Acreate (loc_id, attr_name,
1177 H5T_NATIVE_UCHAR, as_id, octave_H5P_DEFAULT);
1178 #endif
1179 if (a_id >= 0)
1180 {
1181 unsigned char attr_val = 1;
1182
1183 retval = H5Awrite (a_id, H5T_NATIVE_UCHAR, &attr_val);
1184
1185 H5Aclose (a_id);
1186 }
1187 else
1188 retval = a_id;
1189
1190 H5Sclose (as_id);
1191 }
1192 else
1193 retval = as_id;
1194
1195 return retval;
1196
1197 #else
1198 err_disabled_feature ("hdf5_add_attr", "HDF5");
1199 #endif
1200 }
1201
1202 octave_hdf5_err
hdf5_add_scalar_attr(octave_hdf5_id loc_id,octave_hdf5_id type_id,const char * attr_name,void * buf)1203 hdf5_add_scalar_attr (octave_hdf5_id loc_id, octave_hdf5_id type_id,
1204 const char *attr_name, void *buf)
1205 {
1206 #if defined (HAVE_HDF5)
1207
1208 herr_t retval = 0;
1209
1210 hid_t as_id = H5Screate (H5S_SCALAR);
1211
1212 if (as_id >= 0)
1213 {
1214 #if defined (HAVE_HDF5_18)
1215 hid_t a_id = H5Acreate (loc_id, attr_name, type_id,
1216 as_id, octave_H5P_DEFAULT, octave_H5P_DEFAULT);
1217 #else
1218 hid_t a_id = H5Acreate (loc_id, attr_name,
1219 type_id, as_id, octave_H5P_DEFAULT);
1220 #endif
1221 if (a_id >= 0)
1222 {
1223 retval = H5Awrite (a_id, type_id, buf);
1224
1225 H5Aclose (a_id);
1226 }
1227 else
1228 retval = a_id;
1229
1230 H5Sclose (as_id);
1231 }
1232 else
1233 retval = as_id;
1234
1235 return retval;
1236
1237 #else
1238 err_disabled_feature ("hdf5_add_scalar_attr", "HDF5");
1239 #endif
1240 }
1241
1242 // Save an empty matrix, if needed. Returns
1243 // > 0 Saved empty matrix
1244 // = 0 Not an empty matrix; did nothing
1245 // < 0 Error condition
1246 int
save_hdf5_empty(octave_hdf5_id loc_id,const char * name,const dim_vector & d)1247 save_hdf5_empty (octave_hdf5_id loc_id, const char *name, const dim_vector& d)
1248 {
1249 #if defined (HAVE_HDF5)
1250
1251 hsize_t sz = d.length ();
1252 OCTAVE_LOCAL_BUFFER (octave_idx_type, dims, sz);
1253 bool empty = false;
1254 hid_t space_hid = -1;
1255 hid_t data_hid = -1;
1256 int retval;
1257 for (hsize_t i = 0; i < sz; i++)
1258 {
1259 dims[i] = d(i);
1260 if (dims[i] < 1)
1261 empty = true;
1262 }
1263
1264 if (! empty)
1265 return 0;
1266
1267 space_hid = H5Screate_simple (1, &sz, nullptr);
1268 if (space_hid < 0) return space_hid;
1269 #if defined (HAVE_HDF5_18)
1270 data_hid = H5Dcreate (loc_id, name, H5T_NATIVE_IDX, space_hid,
1271 octave_H5P_DEFAULT, octave_H5P_DEFAULT, octave_H5P_DEFAULT);
1272 #else
1273 data_hid = H5Dcreate (loc_id, name, H5T_NATIVE_IDX, space_hid,
1274 octave_H5P_DEFAULT);
1275 #endif
1276 if (data_hid < 0)
1277 {
1278 H5Sclose (space_hid);
1279 return data_hid;
1280 }
1281
1282 retval = H5Dwrite (data_hid, H5T_NATIVE_IDX, octave_H5S_ALL, octave_H5S_ALL,
1283 octave_H5P_DEFAULT, dims) >= 0;
1284
1285 H5Dclose (data_hid);
1286 H5Sclose (space_hid);
1287
1288 if (retval)
1289 retval = hdf5_add_attr (loc_id, "OCTAVE_EMPTY_MATRIX");
1290
1291 return (retval == 0 ? 1 : retval);
1292
1293 #else
1294 err_disabled_feature ("save_hdf5_empty", "HDF5");
1295 #endif
1296 }
1297
1298 // Load an empty matrix, if needed. Returns
1299 // > 0 loaded empty matrix, dimensions returned
1300 // = 0 Not an empty matrix; did nothing
1301 // < 0 Error condition
1302 int
load_hdf5_empty(octave_hdf5_id loc_id,const char * name,dim_vector & d)1303 load_hdf5_empty (octave_hdf5_id loc_id, const char *name, dim_vector& d)
1304 {
1305 #if defined (HAVE_HDF5)
1306
1307 if (! hdf5_check_attr (loc_id, "OCTAVE_EMPTY_MATRIX"))
1308 return 0;
1309
1310 hsize_t hdims, maxdims;
1311 #if defined (HAVE_HDF5_18)
1312 hid_t data_hid = H5Dopen (loc_id, name, octave_H5P_DEFAULT);
1313 #else
1314 hid_t data_hid = H5Dopen (loc_id, name);
1315 #endif
1316 hid_t space_id = H5Dget_space (data_hid);
1317 H5Sget_simple_extent_dims (space_id, &hdims, &maxdims);
1318 int retval;
1319
1320 OCTAVE_LOCAL_BUFFER (octave_idx_type, dims, hdims);
1321
1322 retval = H5Dread (data_hid, H5T_NATIVE_IDX, octave_H5S_ALL, octave_H5S_ALL,
1323 octave_H5P_DEFAULT, dims);
1324 if (retval >= 0)
1325 {
1326 d.resize (hdims);
1327 for (hsize_t i = 0; i < hdims; i++)
1328 d(i) = dims[i];
1329 }
1330
1331 H5Sclose (space_id);
1332 H5Dclose (data_hid);
1333
1334 return (retval == 0 ? hdims : retval);
1335
1336 #else
1337 err_disabled_feature ("load_hdf5_empty", "HDF5");
1338 #endif
1339 }
1340
1341 // save_type_to_hdf5 is not currently used, since hdf5 doesn't yet support
1342 // automatic float<->integer conversions:
1343
1344 // return the HDF5 type id corresponding to the Octave save_type
1345
1346 octave_hdf5_id
save_type_to_hdf5(save_type st)1347 save_type_to_hdf5 (save_type st)
1348 {
1349 #if defined (HAVE_HDF5)
1350 # if defined (HAVE_HDF5_INT2FLOAT_CONVERSIONS)
1351
1352 switch (st)
1353 {
1354 case LS_U_CHAR:
1355 return H5T_NATIVE_UCHAR;
1356
1357 case LS_U_SHORT:
1358 return H5T_NATIVE_USHORT;
1359
1360 case LS_U_INT:
1361 return H5T_NATIVE_UINT;
1362
1363 case LS_CHAR:
1364 return H5T_NATIVE_CHAR;
1365
1366 case LS_SHORT:
1367 return H5T_NATIVE_SHORT;
1368
1369 case LS_INT:
1370 return H5T_NATIVE_INT;
1371
1372 case LS_FLOAT:
1373 return H5T_NATIVE_FLOAT;
1374
1375 case LS_DOUBLE:
1376 default:
1377 return H5T_NATIVE_DOUBLE;
1378 }
1379
1380 # else
1381
1382 octave_unused_parameter (st);
1383
1384 return -1;
1385
1386 # endif
1387
1388 #else
1389
1390 octave_unused_parameter (st);
1391
1392 err_disabled_feature ("save_type_to_hdf5", "HDF5");
1393
1394 #endif
1395 }
1396
1397 // Add the data from TC to the HDF5 location loc_id, which could
1398 // be either a file or a group within a file. Return true if
1399 // successful. This function calls itself recursively for lists
1400 // (stored as HDF5 groups).
1401
1402 bool
add_hdf5_data(octave_hdf5_id loc_id,const octave_value & tc,const std::string & name,const std::string & doc,bool mark_global,bool save_as_floats)1403 add_hdf5_data (octave_hdf5_id loc_id, const octave_value& tc,
1404 const std::string& name, const std::string& doc,
1405 bool mark_global, bool save_as_floats)
1406 {
1407 #if defined (HAVE_HDF5)
1408
1409 hsize_t dims[3];
1410 hid_t type_id, space_id, data_id, data_type_id;
1411 type_id = space_id = data_id = data_type_id = -1;
1412
1413 bool retval = false;
1414 octave_value val = tc;
1415 // FIXME: diagonal & permutation matrices currently don't know how to save
1416 // themselves, so we convert them first to normal matrices using A = A(:,:).
1417 // This is a temporary hack.
1418 if (val.is_diag_matrix () || val.is_perm_matrix ()
1419 || val.type_id () == octave_lazy_index::static_type_id ())
1420 val = val.full_value ();
1421
1422 std::string t = val.type_name ();
1423 #if defined (HAVE_HDF5_18)
1424 data_id = H5Gcreate (loc_id, name.c_str (), octave_H5P_DEFAULT,
1425 octave_H5P_DEFAULT, octave_H5P_DEFAULT);
1426 #else
1427 data_id = H5Gcreate (loc_id, name.c_str (), 0);
1428 #endif
1429 if (data_id < 0)
1430 goto error_cleanup;
1431
1432 // attach the type of the variable
1433 type_id = H5Tcopy (H5T_C_S1); H5Tset_size (type_id, t.length () + 1);
1434 if (type_id < 0)
1435 goto error_cleanup;
1436
1437 dims[0] = 0;
1438 space_id = H5Screate_simple (0, dims, nullptr);
1439 if (space_id < 0)
1440 goto error_cleanup;
1441 #if defined (HAVE_HDF5_18)
1442 data_type_id = H5Dcreate (data_id, "type", type_id, space_id,
1443 octave_H5P_DEFAULT, octave_H5P_DEFAULT,
1444 octave_H5P_DEFAULT);
1445 #else
1446 data_type_id = H5Dcreate (data_id, "type", type_id, space_id,
1447 octave_H5P_DEFAULT);
1448 #endif
1449 if (data_type_id < 0
1450 || H5Dwrite (data_type_id, type_id, octave_H5S_ALL, octave_H5S_ALL,
1451 octave_H5P_DEFAULT, t.c_str ()) < 0)
1452 goto error_cleanup;
1453
1454 // Now call the real function to save the variable
1455 retval = val.save_hdf5 (data_id, "value", save_as_floats);
1456
1457 // attach doc string as comment:
1458 if (retval && doc.length () > 0
1459 && H5Gset_comment (loc_id, name.c_str (), doc.c_str ()) < 0)
1460 retval = false;
1461
1462 // if it's global, add an attribute "OCTAVE_GLOBAL" with value 1
1463 if (retval && mark_global)
1464 retval = hdf5_add_attr (data_id, "OCTAVE_GLOBAL") >= 0;
1465
1466 // We are saving in the new variable format, so mark it
1467 if (retval)
1468 retval = hdf5_add_attr (data_id, "OCTAVE_NEW_FORMAT") >= 0;
1469
1470 error_cleanup:
1471
1472 if (data_type_id >= 0)
1473 H5Dclose (data_type_id);
1474
1475 if (type_id >= 0)
1476 H5Tclose (type_id);
1477
1478 if (space_id >= 0)
1479 H5Sclose (space_id);
1480
1481 if (data_id >= 0)
1482 H5Gclose (data_id);
1483
1484 if (! retval)
1485 error ("save: error while writing '%s' to hdf5 file", name.c_str ());
1486
1487 return retval;
1488
1489 #else
1490 err_disabled_feature ("add_hdf5_data", "HDF5");
1491 #endif
1492 }
1493
1494 // Write data from TC in HDF5 (binary) format to the stream OS,
1495 // which must be an hdf5_ofstream, returning true on success.
1496
1497 bool
save_hdf5_data(std::ostream & os,const octave_value & tc,const std::string & name,const std::string & doc,bool mark_global,bool save_as_floats)1498 save_hdf5_data (std::ostream& os, const octave_value& tc,
1499 const std::string& name, const std::string& doc,
1500 bool mark_global, bool save_as_floats)
1501 {
1502 #if defined (HAVE_HDF5)
1503
1504 check_hdf5_types ();
1505
1506 hdf5_ofstream& hs = dynamic_cast<hdf5_ofstream&> (os);
1507
1508 return add_hdf5_data (hs.file_id, tc, name, doc,
1509 mark_global, save_as_floats);
1510
1511 #else
1512 err_disabled_feature ("save_hdf5_data", "HDF5");
1513 #endif
1514 }
1515
1516 #endif
1517