comparison src/load-save.cc @ 3687:b5a285d1c1f4

[project @ 2000-06-29 21:33:00 by jwe]
author jwe
date Thu, 29 Jun 2000 21:33:01 +0000
parents 19e1ac7359fb
children 8aea513ff224
comparison
equal deleted inserted replaced
3686:9507d6de9bbd 3687:b5a285d1c1f4
17 You should have received a copy of the GNU General Public License 17 You should have received a copy of the GNU General Public License
18 along with Octave; see the file COPYING. If not, write to the Free 18 along with Octave; see the file COPYING. If not, write to the Free
19 Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 19 Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 20
21 */ 21 */
22
23 // Written by John W. Eaton.
24 // HDF5 support by Steven G. Johnson.
22 25
23 #ifdef HAVE_CONFIG_H 26 #ifdef HAVE_CONFIG_H
24 #include <config.h> 27 #include <config.h>
25 #endif 28 #endif
26 29
31 #include <iomanip> 34 #include <iomanip>
32 #include <iostream> 35 #include <iostream>
33 #include <fstream> 36 #include <fstream>
34 #include <strstream> 37 #include <strstream>
35 #include <string> 38 #include <string>
39
40 #ifdef HAVE_HDF5
41 #include <hdf5.h>
42 #endif
36 43
37 #include "byte-swap.h" 44 #include "byte-swap.h"
38 #include "data-conv.h" 45 #include "data-conv.h"
39 #include "file-ops.h" 46 #include "file-ops.h"
40 #include "glob-match.h" 47 #include "glob-match.h"
47 #include "defun.h" 54 #include "defun.h"
48 #include "error.h" 55 #include "error.h"
49 #include "gripes.h" 56 #include "gripes.h"
50 #include "load-save.h" 57 #include "load-save.h"
51 #include "oct-obj.h" 58 #include "oct-obj.h"
59 #include "oct-map.h"
52 #include "pager.h" 60 #include "pager.h"
53 #include "pt-exp.h" 61 #include "pt-exp.h"
54 #include "symtab.h" 62 #include "symtab.h"
55 #include "sysdep.h" 63 #include "sysdep.h"
56 #include "unwind-prot.h" 64 #include "unwind-prot.h"
59 #include "version.h" 67 #include "version.h"
60 68
61 // Write octave-core file if Octave crashes or is killed by a signal. 69 // Write octave-core file if Octave crashes or is killed by a signal.
62 static bool Vcrash_dumps_octave_core; 70 static bool Vcrash_dumps_octave_core;
63 71
64 // The default output format. May be one of "binary", "text", or 72 // The default output format. May be one of "binary", "text",
65 // "mat-binary". 73 // "mat-binary", or "hdf5".
66 static std::string Vdefault_save_format; 74 static std::string Vdefault_save_format;
67 75
68 // The number of decimal digits to use when writing ascii data. 76 // The number of decimal digits to use when writing ascii data.
69 static int Vsave_precision; 77 static int Vsave_precision;
70 78
78 { 86 {
79 LS_ASCII, 87 LS_ASCII,
80 LS_BINARY, 88 LS_BINARY,
81 LS_MAT_ASCII, 89 LS_MAT_ASCII,
82 LS_MAT_BINARY, 90 LS_MAT_BINARY,
91 #ifdef HAVE_HDF5
92 LS_HDF5,
93 #endif /* HAVE_HDF5 */
83 LS_UNKNOWN 94 LS_UNKNOWN
84 }; 95 };
85 96
86 // Return TRUE if S is a valid identifier. 97 // Return TRUE if S is a valid identifier.
87 98
101 static bool 112 static bool
102 valid_identifier (const std::string& s) 113 valid_identifier (const std::string& s)
103 { 114 {
104 return valid_identifier (s.c_str ()); 115 return valid_identifier (s.c_str ());
105 } 116 }
117
118 #ifdef HAVE_HDF5
119 // this is only used for HDF5 import
120 // try to convert s into a valid identifier, replacing invalid chars with "_":
121 static void
122 make_valid_identifier (char *s)
123 {
124 if (s)
125 {
126 for (; *s; ++s)
127 if (! (isalnum (*s) || *s == '_'))
128 *s = '_';
129 }
130 }
131 #endif /* HAVE_HDF5 */
106 132
107 // XXX FIXME XXX -- shouldn't this be implemented in terms of other 133 // XXX FIXME XXX -- shouldn't this be implemented in terms of other
108 // functions that are already available? 134 // functions that are already available?
109 135
110 // Install a variable with name NAME and the value specified TC in the 136 // Install a variable with name NAME and the value specified TC in the
327 if (len > 0) 353 if (len > 0)
328 { 354 {
329 char *ptr = retval + len - 1; 355 char *ptr = retval + len - 1;
330 while (*ptr == ' ' || *ptr == '\t') 356 while (*ptr == ' ' || *ptr == '\t')
331 ptr--; 357 ptr--;
332 *(ptr+1) = '\0'; 358 * (ptr+1) = '\0';
333 } 359 }
334 } 360 }
335 361
336 return retval; 362 return retval;
337 } 363 }
647 else 673 else
648 error ("load: failed to extract string length"); 674 error ("load: failed to extract string length");
649 } 675 }
650 else if (strncmp (ptr, "range", 5) == 0) 676 else if (strncmp (ptr, "range", 5) == 0)
651 { 677 {
652 // # base, limit, range comment added by save(). 678 // # base, limit, range comment added by save ().
653 679
654 skip_comments (is); 680 skip_comments (is);
655 Range tmp; 681 Range tmp;
656 is >> tmp; 682 is >> tmp;
657 if (is) 683 if (is)
951 } 977 }
952 978
953 return name; 979 return name;
954 } 980 }
955 981
982 // HDF5 input/output
983
984 #ifdef HAVE_HDF5
985
986 // Define this to 1 if/when HDF5 supports automatic conversion between
987 // integer and floating-point binary data:
988 #define HAVE_HDF5_INT2FLOAT_CONVERSIONS 0
989
990 // first, we need to define our own dummy stream subclass, since
991 // HDF5 needs to do its own file i/o
992
993 // hdf5_fstreambase is used for both input and output streams, modeled
994 // on the fstreambase class in <fstream.h>
995
996 class hdf5_fstreambase : virtual public std::ios
997 {
998 public:
999
1000 // HDF5 uses an "id" to refer to an open file
1001 hid_t file_id;
1002
1003 // keep track of current item index in the file
1004 int current_item;
1005
1006 hdf5_fstreambase () { file_id = -1; }
1007
1008 hdf5_fstreambase (const char *name, int mode, int prot = 0)
1009 {
1010 if (mode == std::ios::in)
1011 file_id = H5Fopen (name, H5F_ACC_RDONLY, H5P_DEFAULT);
1012 else if (mode == std::ios::out)
1013 file_id = H5Fcreate (name, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
1014
1015 if (file_id < 0)
1016 set (std::ios::badbit);
1017
1018 current_item = 0;
1019 }
1020
1021 void close ()
1022 {
1023 if (file_id >= 0)
1024 {
1025 if (H5Fclose (file_id) < 0)
1026 set (std::ios::badbit);
1027 file_id = -1;
1028 }
1029 }
1030
1031 void open (const char *name, int mode, int prot = 0)
1032 {
1033 clear ();
1034
1035 if (mode == std::ios::in)
1036 file_id = H5Fopen (name, H5F_ACC_RDONLY, H5P_DEFAULT);
1037 else if (mode == std::ios::out)
1038 file_id = H5Fcreate (name, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
1039
1040 if (file_id < 0)
1041 set (std::ios::badbit);
1042
1043 current_item = 0;
1044 }
1045 };
1046
1047 // input and output streams, subclassing istream and ostream
1048 // so that we can pass them for stream parameters in the functions below.
1049
1050 class hdf5_ifstream : public hdf5_fstreambase, public std::istream
1051 {
1052 public:
1053
1054 hdf5_ifstream () : hdf5_fstreambase () { }
1055
1056 hdf5_ifstream (const char *name, int mode = std::ios::in, int prot = 0)
1057 : hdf5_fstreambase (name, mode, prot) { }
1058
1059 void open (const char *name, int mode = std::ios::in, int prot = 0)
1060 { hdf5_fstreambase::open (name, mode, prot); }
1061 };
1062
1063 class hdf5_ofstream : public hdf5_fstreambase, public std::ostream
1064 {
1065 public:
1066
1067 hdf5_ofstream () : hdf5_fstreambase () { }
1068
1069 hdf5_ofstream (const char *name, int mode = std::ios::out, int prot = 0)
1070 : hdf5_fstreambase (name, mode, prot) { }
1071
1072 void open (const char *name, int mode = std::ios::out, int prot = 0)
1073 { hdf5_fstreambase::open (name, mode, prot); }
1074 };
1075
1076 // Given two compound types t1 and t2, determine whether they
1077 // are compatible for reading/writing. This function only
1078 // works for non-nested types composed of simple elements (ints, floats...),
1079 // which is all we need it for
1080
1081 bool
1082 hdf5_types_compatible (hid_t t1, hid_t t2)
1083 {
1084 int n;
1085 if ((n = H5Tget_nmembers (t1)) != H5Tget_nmembers (t2))
1086 return false;
1087
1088 for (int i = 0; i < n; ++i)
1089 {
1090 hid_t mt1 = H5Tget_member_type (t1, i);
1091 hid_t mt2 = H5Tget_member_type (t2, i);
1092
1093 if (H5Tget_class (mt1) != H5Tget_class (mt2))
1094 return false;
1095
1096 H5Tclose (mt2);
1097 H5Tclose (mt1);
1098 }
1099
1100 return true;
1101 }
1102
1103 // Import a multidimensional (rank >= 3) dataset whose id is data_id, into tc.
1104 // This works by calling itself recursively, building up lists of lists
1105 // of lists ... of 2d matrices. rank and dims are the rank and dimensions
1106 // of the dataset. type_id is the datatype to read into. If it is
1107 // H5T_NATIVE_DOUBLE, we are reading a real matrix. Otherwise, type_id
1108 // is assumed to be a complex type for reading a complex matrix.
1109 //
1110 // Upon entry, we should have curdim = rank - 1, start = an array
1111 // of length rank = all zeros, and count = an array of length rank =
1112 // all ones except for the first two dimensions which equal the corresponding
1113 // entries in dims[].
1114 //
1115 // Note that we process the dimensions in reverse order, reflecting
1116 // the fact that Octave is uses column-major (Fortran-order) data while
1117 // HDF5 is row-major. This means that the HDF5 file is read
1118 // non-contiguously, but on the other hand means that for a 3d array
1119 // we get a list of xy-plane slices, which seems nice. We could change
1120 // this behavior without much trouble; what is the best thing to do?
1121 //
1122 // Returns a positive value upon success.
1123
1124 static herr_t
1125 hdf5_import_multidim (hid_t data_id, hid_t space_id, hsize_t rank,
1126 const hsize_t *dims, hsize_t curdim,
1127 hssize_t *start, const hsize_t *count,
1128 hid_t type_id, octave_value &tc)
1129 {
1130 herr_t retval = 1;
1131
1132 if (rank < 3 || curdim < 1 || curdim >= rank)
1133 return -1;
1134
1135 if (curdim == 1)
1136 {
1137 // import 2d dataset for 1st 2 dims directly as a matrix
1138 int nr, nc; // rows and columns
1139 nc = dims[0]; // octave uses column-major & HDF5 uses row-major
1140 nr = dims[1];
1141
1142 hid_t mem_space_id = H5Screate_simple (2, dims, NULL);
1143
1144 if (mem_space_id < 0)
1145 return -1;
1146
1147 if (H5Sselect_all (mem_space_id) < 0)
1148 return -1;
1149
1150 if (H5Sselect_hyperslab (space_id, H5S_SELECT_SET,
1151 start, NULL, count, NULL) < 0)
1152 {
1153 H5Sclose (mem_space_id);
1154 return -1;
1155 }
1156
1157 if (type_id == H5T_NATIVE_DOUBLE)
1158 {
1159 // real matrix
1160 Matrix m (nr, nc);
1161 double *re = m.fortran_vec ();
1162 if (H5Dread (data_id, type_id, mem_space_id, space_id,
1163 H5P_DEFAULT, (void *) re) < 0)
1164 retval = -1; // error
1165 else
1166 tc = m;
1167 }
1168 else
1169 {
1170 // assume that we are using complex numbers
1171 // complex matrix
1172 ComplexMatrix m (nr, nc);
1173 Complex *reim = m.fortran_vec ();
1174 if (H5Dread (data_id, type_id, mem_space_id, space_id,
1175 H5P_DEFAULT, (void *) X_CAST (double *, reim)) < 0)
1176 retval = -1; // error
1177 else
1178 tc = m;
1179 }
1180
1181 H5Sclose (mem_space_id);
1182
1183 }
1184 else
1185 {
1186 octave_value_list lst;
1187
1188 for (hsize_t i = 0; i < dims[curdim]; ++i)
1189 {
1190 octave_value slice;
1191 start[curdim] = i;
1192 retval = hdf5_import_multidim (data_id, space_id, rank,
1193 dims, curdim-1, start, count,
1194 type_id, slice);
1195 if (retval < 0)
1196 break;
1197 lst.append (slice);
1198 }
1199
1200 if (retval > 0)
1201 tc = lst;
1202 }
1203
1204 return retval;
1205 }
1206
1207 // Return true if loc_id has the attribute named attr_name, and false
1208 // otherwise.
1209
1210 bool
1211 hdf5_check_attr (hid_t loc_id, const char *attr_name)
1212 {
1213 bool retval = false;
1214
1215 // we have to pull some shenanigans here to make sure
1216 // HDF5 doesn't print out all sorts of error messages if we
1217 // call H5Aopen for a non-existing attribute
1218
1219 H5E_auto_t err_func;
1220 void *err_func_data;
1221
1222 // turn off error reporting temporarily, but save the error
1223 // reporting function:
1224
1225 H5Eget_auto (&err_func, &err_func_data);
1226 H5Eset_auto (NULL, NULL);
1227
1228 hid_t attr_id = H5Aopen_name(loc_id, attr_name);
1229
1230 if (attr_id >= 0)
1231 {
1232 // successful
1233 retval = 1;
1234 H5Aclose (attr_id);
1235 }
1236
1237 // restore error reporting:
1238 H5Eset_auto (err_func, err_func_data);
1239
1240 return retval;
1241 }
1242
1243 // Callback data structure for passing data to hdf5_read_next_data, below.
1244
1245 struct hdf5_callback_data
1246 {
1247 // the following fields are set by hdf5_read_data on successful return:
1248
1249 // the name of the variable
1250 char *name;
1251
1252 // whether it is global
1253 bool global;
1254
1255 // the value of the variable, in Octave form
1256 octave_value tc;
1257
1258 // a documentation string (NULL if none)
1259 char *doc;
1260
1261 // the following fields are input to hdf5_read_data:
1262
1263 // HDF5 rep's of complex and range type
1264 hid_t complex_type, range_type;
1265
1266 // whether to try extra hard to import "foreign" data
1267 bool import;
1268 };
1269
1270 // This function is designed to be passed to H5Giterate, which calls it
1271 // on each data item in an HDF5 file. For the item whose name is NAME in
1272 // the group GROUP_ID, this function sets dv->tc to an Octave representation
1273 // of that item. (dv must be a pointer to hdf5_callback_data.) (It also
1274 // sets the other fields of dv).
1275 //
1276 // It returns 1 on success (in which case H5Giterate stops and returns),
1277 // -1 on error, and 0 to tell H5Giterate to continue on to the next item
1278 // (e.g. if NAME was a data type we don't recognize).
1279
1280 static herr_t
1281 hdf5_read_next_data (hid_t group_id, const char *name, void *dv)
1282 {
1283 hdf5_callback_data *d = (hdf5_callback_data *) dv;
1284 H5G_stat_t info;
1285 herr_t retval = 0;
1286 bool ident_valid = valid_identifier (name);
1287 char *vname;
1288
1289 vname = new char[strlen (name) + 1];
1290
1291 strcpy (vname, name);
1292
1293 if (!ident_valid && d->import)
1294 {
1295 // fix the identifier, replacing invalid chars with underscores
1296 make_valid_identifier (vname);
1297
1298 // check again (in case vname was null, empty, or some such thing):
1299 ident_valid = valid_identifier (vname);
1300 }
1301
1302 H5Gget_objinfo (group_id, name, 1, &info);
1303
1304 if (info.type == H5G_DATASET && ident_valid)
1305 {
1306 retval = 1;
1307
1308 hid_t data_id = H5Dopen (group_id, name);
1309
1310 if (data_id < 0)
1311 {
1312 retval = data_id;
1313
1314 goto done;
1315 }
1316
1317 hid_t type_id = H5Dget_type (data_id);
1318
1319 hid_t type_class_id = H5Tget_class (type_id);
1320
1321 #if HAVE_HDF5_INT2FLOAT_CONVERSIONS
1322 if (type_class_id == H5T_INTEGER || type_class_id == H5T_FLOAT)
1323 {
1324 #else
1325 // hdf5 doesn't (yet) support automatic float/integer conversions
1326 if (type_class_id == H5T_FLOAT)
1327 {
1328 #endif
1329 // read real matrix or scalar variable
1330
1331 hid_t space_id = H5Dget_space (data_id);
1332
1333 hsize_t rank = H5Sget_simple_extent_ndims (space_id);
1334
1335 if (rank == 0)
1336 {
1337 // real scalar:
1338 double dtmp;
1339 if (H5Dread (data_id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL,
1340 H5P_DEFAULT, (void *) &dtmp) < 0)
1341 retval = -1; // error
1342 else
1343 d->tc = dtmp;
1344 }
1345 else if (rank > 0 && rank <= 2)
1346 {
1347 // real matrix
1348 hsize_t *dims = new hsize_t[rank];
1349 hsize_t *maxdims = new hsize_t[rank];
1350
1351 H5Sget_simple_extent_dims (space_id, dims, maxdims);
1352
1353 int nr, nc; // rows and columns
1354 // octave uses column-major & HDF5 uses row-major
1355 nc = dims[0];
1356 nr = rank > 1 ? dims[1] : 1;
1357
1358 delete [] dims;
1359 delete [] maxdims;
1360
1361 Matrix m (nr, nc);
1362 double *re = m.fortran_vec ();
1363 if (H5Dread (data_id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL,
1364 H5P_DEFAULT, (void *) re) < 0)
1365 retval = -1; // error
1366 else
1367 d->tc = m;
1368 }
1369 else if (rank >= 3 && d->import)
1370 {
1371 hsize_t *dims = new hsize_t[rank];
1372 hsize_t *maxdims = new hsize_t[rank];
1373
1374 H5Sget_simple_extent_dims (space_id, dims, maxdims);
1375
1376 hssize_t *start = new hssize_t[rank];
1377 hsize_t *count = new hsize_t[rank];
1378
1379 for (hsize_t i = 0; i < rank; ++i)
1380 {
1381 start[i] = 0;
1382 count[i] = 1;
1383 }
1384 count[0] = dims[0];
1385 count[1] = dims[1];
1386 retval = hdf5_import_multidim (data_id, space_id,
1387 rank, dims, rank-1,
1388 start, count,
1389 H5T_NATIVE_DOUBLE, d->tc);
1390
1391 delete [] count;
1392 delete [] start;
1393 delete [] dims;
1394 delete [] maxdims;
1395 }
1396 else
1397 {
1398 warning ("load: can't read %d-dim. hdf5 dataset %s",
1399 rank, name);
1400 retval = 0; // skip; we can't read 3+ dimensional datasets
1401 }
1402
1403 H5Sclose (space_id);
1404 }
1405 else if (type_class_id == H5T_STRING)
1406 {
1407 // read string variable
1408 hid_t space_id = H5Dget_space (data_id);
1409 hsize_t rank = H5Sget_simple_extent_ndims (space_id);
1410
1411 if (rank == 0)
1412 {
1413 // a single string:
1414 int slen = H5Tget_size (type_id);
1415 if (slen < 0)
1416 retval = -1; // error
1417 else
1418 {
1419 char *s = new char [slen];
1420 // create datatype for (null-terminated) string
1421 // to read into:
1422 hid_t st_id = H5Tcopy (H5T_C_S1);
1423 H5Tset_size (st_id, slen);
1424 if (H5Dread (data_id, st_id, H5S_ALL, H5S_ALL,
1425 H5P_DEFAULT, (void *) s) < 0)
1426 {
1427 delete [] s;
1428 retval = -1; // error
1429 }
1430 else
1431 d->tc = s;
1432
1433 H5Tclose (st_id);
1434 }
1435 }
1436 else if (rank == 1)
1437 {
1438 // string vector
1439 hsize_t elements, maxdim;
1440 H5Sget_simple_extent_dims (space_id, &elements, &maxdim);
1441 int slen = H5Tget_size (type_id);
1442 if (slen < 0)
1443 retval = -1; // error
1444 else
1445 {
1446 // hdf5 string arrays store strings of all the
1447 // same physical length (I think), which is
1448 // slightly wasteful, but oh well.
1449
1450 char *s = new char [elements * slen];
1451 // create datatype for (null-terminated) string
1452 // to read into:
1453 hid_t st_id = H5Tcopy (H5T_C_S1);
1454 H5Tset_size (st_id, slen);
1455
1456 if (H5Dread (data_id, st_id, H5S_ALL, H5S_ALL,
1457 H5P_DEFAULT, (void *) s) < 0)
1458 retval = -1; // error
1459 else
1460 {
1461 charMatrix chm (elements, slen - 1);
1462 for (hsize_t i = 0; i < elements; ++i)
1463 {
1464 chm.insert (s + i*slen, i, 0);
1465 }
1466 d->tc = octave_value (chm, true);
1467 }
1468
1469 delete [] s;
1470
1471 H5Tclose (st_id);
1472 }
1473 }
1474 else
1475 {
1476 warning ("load: can't read %d-dim. hdf5 string vector %s",
1477 rank, name);
1478 // skip; we can't read higher-dimensional string vectors
1479 retval = 0;
1480 }
1481 }
1482 else if (type_class_id == H5T_COMPOUND)
1483 {
1484 // check for complex or range data:
1485
1486 if (hdf5_types_compatible (type_id, d->complex_type))
1487 {
1488 // read complex matrix or scalar variable
1489
1490 hid_t space_id = H5Dget_space (data_id);
1491 hsize_t rank = H5Sget_simple_extent_ndims (space_id);
1492
1493 if (rank == 0)
1494 {
1495 // complex scalar:
1496 Complex ctmp;
1497 if (H5Dread (data_id, d->complex_type, H5S_ALL,
1498 H5S_ALL, H5P_DEFAULT,
1499 (void *) X_CAST (double *, &ctmp)) < 0)
1500 retval = -1; // error
1501 else
1502 d->tc = ctmp;
1503 }
1504 else if (rank > 0 && rank <= 2)
1505 {
1506 // complex matrix
1507 hsize_t *dims = new hsize_t[rank];
1508 hsize_t *maxdims = new hsize_t[rank];
1509 H5Sget_simple_extent_dims (space_id, dims, maxdims);
1510 int nr, nc; // rows and columns
1511 // octave uses column-major & HDF5 uses row-major
1512 nc = dims[0];
1513 nr = rank > 1 ? dims[1] : 1;
1514 delete [] dims;
1515 delete [] maxdims;
1516 ComplexMatrix m (nr, nc);
1517 Complex *reim = m.fortran_vec ();
1518 if (H5Dread (data_id, d->complex_type, H5S_ALL,
1519 H5S_ALL, H5P_DEFAULT,
1520 (void *) X_CAST (double *, reim)) < 0)
1521 retval = -1; // error
1522 else
1523 d->tc = m;
1524 }
1525 else if (rank >= 3 && d->import)
1526 {
1527 hsize_t *dims = new hsize_t[rank]
1528 hsize_t *maxdims = new hsize_t[rank];
1529 H5Sget_simple_extent_dims (space_id, dims, maxdims);
1530 hssize_t *start = new hssize_t[rank];
1531 hsize_t *count = new hsize_t[rank];
1532 for (hsize_t i = 0; i < rank; ++i)
1533 {
1534 start[i] = 0;
1535 count[i] = 1;
1536 }
1537 count[0] = dims[0];
1538 count[1] = dims[1];
1539 retval = hdf5_import_multidim (data_id, space_id,
1540 rank, dims, rank-1,
1541 start, count,
1542 d->complex_type,
1543 d->tc);
1544
1545 delete [] count;
1546 delete [] start;
1547 delete [] dims;
1548 delete [] maxdims;
1549 }
1550 else
1551 {
1552 warning ("load: can't read %d-dim. hdf5 dataset %s",
1553 rank, name);
1554 // skip; we can't read 3+ dimensional datasets
1555 retval = 0;
1556 }
1557 H5Sclose (space_id);
1558 }
1559 else if (hdf5_types_compatible (type_id, d->range_type))
1560 {
1561 // read range variable:
1562 hid_t space_id = H5Dget_space (data_id);
1563 hsize_t rank = H5Sget_simple_extent_ndims (space_id);
1564
1565 if (rank == 0)
1566 {
1567 double rangevals[3];
1568 if (H5Dread (data_id, d->range_type, H5S_ALL, H5S_ALL,
1569 H5P_DEFAULT, (void *) rangevals) < 0)
1570 retval = -1; // error
1571 else
1572 {
1573 Range r (rangevals[0], rangevals[1], rangevals[2]);
1574 d->tc = r;
1575 }
1576 }
1577 else
1578 {
1579 warning ("load: can't read range array `%s' in hdf5 file",
1580 name);
1581 // skip; we can't read arrays of range variables
1582 retval = 0;
1583 }
1584
1585 H5Sclose (space_id);
1586 }
1587 else
1588 {
1589 warning ("load: can't read `%s' (unknown compound datatype)",
1590 name);
1591 retval = 0; // unknown datatype; skip.
1592 }
1593 }
1594 else
1595 {
1596 warning ("load: can't read `%s' (unknown datatype)", name);
1597 retval = 0; // unknown datatype; skip
1598 }
1599
1600 H5Tclose (type_id);
1601
1602 // check for OCTAVE_GLOBAL attribute:
1603 d->global = hdf5_check_attr(data_id, "OCTAVE_GLOBAL");
1604
1605 H5Dclose (data_id);
1606 }
1607 else if (info.type == H5G_GROUP && ident_valid)
1608 {
1609 // read in group as a list or a structure
1610 retval = 1;
1611
1612 hid_t subgroup_id = H5Gopen (group_id, name);
1613
1614 if (subgroup_id < 0)
1615 {
1616 retval = subgroup_id;
1617 goto done;
1618 }
1619
1620 // an HDF5 group is treated as an octave structure by
1621 // default (since that preserves name information), and an
1622 // octave list otherwise.
1623
1624 bool is_list = hdf5_check_attr(subgroup_id, "OCTAVE_LIST");
1625
1626 hdf5_callback_data dsub;
1627 dsub.name = dsub.doc = (char*) NULL;
1628 dsub.global = 0;
1629 dsub.complex_type = d->complex_type;
1630 dsub.range_type = d->range_type;
1631 dsub.import = d->import;
1632
1633 herr_t retval2;
1634 octave_value_list lst;
1635 Octave_map m;
1636 int current_item = 0;
1637 while ((retval2 = H5Giterate (group_id, name, &current_item,
1638 hdf5_read_next_data, &dsub)) > 0)
1639 {
1640 if (is_list)
1641 lst.append (dsub.tc);
1642 else
1643 m [dsub.name] = dsub.tc;
1644
1645 if (dsub.name)
1646 delete [] dsub.name;
1647
1648 if (dsub.doc)
1649 delete [] dsub.doc;
1650
1651 current_item++; // H5Giterate returned the last index processed
1652 }
1653
1654 if (retval2 < 0)
1655 retval = retval2;
1656 else
1657 {
1658 d->global = hdf5_check_attr(group_id, "OCTAVE_GLOBAL");
1659
1660 if (is_list)
1661 d->tc = lst;
1662 else
1663 d->tc = m;
1664 }
1665
1666 H5Gclose (subgroup_id);
1667 }
1668 else if (! ident_valid)
1669 {
1670 // should we attempt to handle invalid identifiers by converting
1671 // bad characters to '_', say?
1672 warning ("load: skipping invalid identifier `%s' in hdf5 file",
1673 name);
1674 }
1675
1676 done:
1677
1678 if (retval < 0)
1679 error ("load: error while reading hdf5 item %s", name);
1680
1681 if (retval > 0)
1682 {
1683 // get documentation string, if any:
1684 int comment_length = H5Gget_comment (group_id, name, 0, NULL);
1685
1686 if (comment_length > 1)
1687 {
1688 d->doc = new char[comment_length];
1689 H5Gget_comment (group_id, name, comment_length, d->doc);
1690 }
1691 else if (strcmp (name, vname) != 0)
1692 {
1693 // the name was changed by import; store the original name
1694 // as the documentation string:
1695 d->doc = new char [strlen (name) + 1];
1696 strcpy (d->doc, name);
1697 }
1698 else
1699 d->doc = (char *) NULL;
1700
1701 // copy name (actually, vname):
1702 d->name = new char [strlen (vname) + 1];
1703 strcpy (d->name, vname);
1704 }
1705
1706 delete [] vname;
1707
1708 return retval;
1709 }
1710
1711 // The following two subroutines create HDF5 representations of the way
1712 // we will store Octave complex and range types (pairs and triplets of
1713 // floating-point numbers, respectively). NUM_TYPE is the HDF5 numeric
1714 // type to use for storage (e.g. H5T_NATIVE_DOUBLE to save as 'double').
1715 // Note that any necessary conversions are handled automatically by HDF5.
1716
1717 static hid_t
1718 hdf5_make_complex_type (hid_t num_type)
1719 {
1720 hid_t type_id = H5Tcreate (H5T_COMPOUND, sizeof (double) * 2);
1721
1722 H5Tinsert (type_id, "real", 0 * sizeof (double), num_type);
1723 H5Tinsert (type_id, "imag", 1 * sizeof (double), num_type);
1724
1725 return type_id;
1726 }
1727
1728 static hid_t
1729 hdf5_make_range_type (hid_t num_type)
1730 {
1731 hid_t type_id = H5Tcreate (H5T_COMPOUND, sizeof (double) * 3);
1732
1733 H5Tinsert (type_id, "base", 0 * sizeof (double), num_type);
1734 H5Tinsert (type_id, "limit", 1 * sizeof (double), num_type);
1735 H5Tinsert (type_id, "increment", 2 * sizeof (double), num_type);
1736
1737 return type_id;
1738 }
1739
1740 // Read the next Octave variable from the stream IS, which must really be
1741 // an hdf5_ifstream. Return the variable value in tc, its doc string
1742 // in doc, and whether it is global in global. The return value is
1743 // the name of the variable, or NULL if none were found or there was
1744 // and error. If import is true, we try extra hard to import "foreign"
1745 // datasets (not created by Octave), although we usually do a reasonable
1746 // job anyway. (c.f. load -import documentation.)
1747 static char *
1748 read_hdf5_data (std::istream& is,
1749 const std::string& filename, bool& global,
1750 octave_value& tc, char *&doc, bool import)
1751 {
1752 hdf5_ifstream& hs = (hdf5_ifstream&) is;
1753 hdf5_callback_data d;
1754
1755 d.name = (char *) NULL;
1756 d.global = 0;
1757 d.doc = (char *) NULL;
1758 d.complex_type = hdf5_make_complex_type (H5T_NATIVE_DOUBLE);
1759 d.range_type = hdf5_make_range_type (H5T_NATIVE_DOUBLE);
1760 d.import = import;
1761
1762 herr_t retval = H5Giterate (hs.file_id, "/", &hs.current_item,
1763 hdf5_read_next_data, &d);
1764
1765 // H5Giterate sets current_item to the last item processed; we want
1766 // the index of the next item (for the next call to read_hdf5_data)
1767 hs.current_item++;
1768
1769 if (retval > 0)
1770 {
1771 global = d.global;
1772 tc = d.tc;
1773 doc = d.doc;
1774 }
1775 else
1776 {
1777 // an error occurred (retval < 0) or there are no more datasets
1778 // print an error message if retval < 0?
1779 // hdf5_read_next_data already printed one, probably.
1780 }
1781
1782 H5Tclose (d.complex_type);
1783 H5Tclose (d.range_type);
1784
1785 return d.name;
1786 }
1787
1788 #endif /* HAVE_HDF5 */
1789
956 static std::string 1790 static std::string
957 get_mat_data_input_line (std::istream& is) 1791 get_mat_data_input_line (std::istream& is)
958 { 1792 {
959 std::string retval; 1793 std::string retval;
960 1794
1452 } 2286 }
1453 2287
1454 // Return TRUE if NAME matches one of the given globbing PATTERNS. 2288 // Return TRUE if NAME matches one of the given globbing PATTERNS.
1455 2289
1456 static bool 2290 static bool
1457 matches_patterns (const string_vector& patterns, int pat_idx, 2291 matches_patterns (const std::string_vector& patterns, int pat_idx,
1458 int num_pat, const std::string& name) 2292 int num_pat, const std::string& name)
1459 { 2293 {
1460 for (int i = pat_idx; i < num_pat; i++) 2294 for (int i = pat_idx; i < num_pat; i++)
1461 { 2295 {
1462 glob_match pattern (patterns[i]); 2296 glob_match pattern (patterns[i]);
1505 static load_save_format 2339 static load_save_format
1506 get_file_format (const std::string& fname, const std::string& orig_fname) 2340 get_file_format (const std::string& fname, const std::string& orig_fname)
1507 { 2341 {
1508 load_save_format retval = LS_UNKNOWN; 2342 load_save_format retval = LS_UNKNOWN;
1509 2343
2344 #ifdef HAVE_HDF5
2345 // check this before we open the file
2346 if (H5Fis_hdf5 (fname.c_str ()) > 0)
2347 return LS_HDF5;
2348 #endif /* HAVE_HDF5 */
2349
1510 std::ifstream file (fname.c_str ()); 2350 std::ifstream file (fname.c_str ());
1511 2351
1512 if (! file) 2352 if (! file)
1513 { 2353 {
1514 error ("load: couldn't open input file `%s'", orig_fname.c_str ()); 2354 error ("load: couldn't open input file `%s'", orig_fname.c_str ());
1547 else 2387 else
1548 { 2388 {
1549 // Try reading the file as numbers only, determining the 2389 // Try reading the file as numbers only, determining the
1550 // number of rows and columns from the data. We don't 2390 // number of rows and columns from the data. We don't
1551 // even bother to check to see if the first item in the 2391 // even bother to check to see if the first item in the
1552 // file is a number, so that get_complete_line() can 2392 // file is a number, so that get_complete_line () can
1553 // skip any comments that might appear at the top of the 2393 // skip any comments that might appear at the top of the
1554 // file. 2394 // file.
1555 2395
1556 retval = LS_MAT_ASCII; 2396 retval = LS_MAT_ASCII;
1557 } 2397 }
1568 } 2408 }
1569 2409
1570 static octave_value_list 2410 static octave_value_list
1571 do_load (std::istream& stream, const std::string& orig_fname, bool force, 2411 do_load (std::istream& stream, const std::string& orig_fname, bool force,
1572 load_save_format format, oct_mach_info::float_format flt_fmt, 2412 load_save_format format, oct_mach_info::float_format flt_fmt,
1573 bool list_only, bool swap, bool verbose, const string_vector& argv, 2413 bool list_only, bool swap, bool verbose, bool import,
1574 int argv_idx, int argc, int nargout) 2414 const string_vector& argv, int argv_idx, int argc, int nargout)
1575 { 2415 {
1576 octave_value_list retval; 2416 octave_value_list retval;
1577 2417
1578 std::ostrstream output_buf; 2418 std::ostrstream output_buf;
1579 int count = 0; 2419 int count = 0;
1602 2442
1603 case LS_MAT_BINARY: 2443 case LS_MAT_BINARY:
1604 name = read_mat_binary_data (stream, orig_fname, tc); 2444 name = read_mat_binary_data (stream, orig_fname, tc);
1605 break; 2445 break;
1606 2446
2447 #ifdef HAVE_HDF5
2448 case LS_HDF5:
2449 name = read_hdf5_data (stream, orig_fname,
2450 global, tc, doc, import);
2451 break;
2452 #endif /* HAVE_HDF5 */
2453
1607 default: 2454 default:
1608 gripe_unrecognized_data_fmt ("load"); 2455 gripe_unrecognized_data_fmt ("load");
1609 break; 2456 break;
1610 } 2457 }
1611 2458
1620 { 2467 {
1621 if (tc.is_defined ()) 2468 if (tc.is_defined ())
1622 { 2469 {
1623 if (format == LS_MAT_ASCII && argv_idx < argc) 2470 if (format == LS_MAT_ASCII && argv_idx < argc)
1624 warning ("load: loaded ASCII file `%s' -- ignoring extra args", 2471 warning ("load: loaded ASCII file `%s' -- ignoring extra args",
1625 orig_fname.c_str()); 2472 orig_fname.c_str ());
1626 2473
1627 if (format == LS_MAT_ASCII 2474 if (format == LS_MAT_ASCII
1628 || argv_idx == argc 2475 || argv_idx == argc
1629 || matches_patterns (argv, argv_idx, argc, name)) 2476 || matches_patterns (argv, argv_idx, argc, name))
1630 { 2477 {
1692 delete [] msg; 2539 delete [] msg;
1693 } 2540 }
1694 2541
1695 return retval; 2542 return retval;
1696 } 2543 }
2544
2545 // HDF5 load/save documentation is included in the Octave manual
2546 // regardless, but if HDF5 is not linked in we also include a
2547 // sentence noting this, so the user understands that the features
2548 // aren't available. Define a macro for this sentence:
2549
2550 #ifdef HAVE_HDF5
2551 #define HAVE_HDF5_HELP_STRING ""
2552 #else /* ! HAVE_HDF5 */
2553 #define HAVE_HDF5_HELP_STRING "\n\
2554 HDF5 load and save are not available, as this Octave executable was\n\
2555 not linked with the HDF5 library."
2556 #endif /* ! HAVE HDF5 */
1697 2557
1698 DEFUN_TEXT (load, args, nargout, 2558 DEFUN_TEXT (load, args, nargout,
1699 "-*- texinfo -*-\n\ 2559 "-*- texinfo -*-\n\
1700 @deffn {Command} load options file v1 v2 @dots{}\n\ 2560 @deffn {Command} load options file v1 v2 @dots{}\n\
1701 Load the named variables from the file @var{file}. As with @code{save},\n\ 2561 Load the named variables from the file @var{file}. As with @code{save},\n\
1737 @item -binary\n\ 2597 @item -binary\n\
1738 Force Octave to assume the file is in Octave's binary format.\n\ 2598 Force Octave to assume the file is in Octave's binary format.\n\
1739 \n\ 2599 \n\
1740 @item -mat-binary\n\ 2600 @item -mat-binary\n\
1741 Force Octave to assume the file is in @sc{Matlab}'s binary format.\n\ 2601 Force Octave to assume the file is in @sc{Matlab}'s binary format.\n\
2602 \n\
2603 @item -hdf5\n\
2604 Force Octave to assume the file is in HDF5 format.\n\
2605 (HDF5 is a free, portable binary format developed by the National\n\
2606 Center for Supercomputing Applications at the University of Illinois.)\n\
2607 Note that Octave can read HDF5 files not created by itself, but may\n\
2608 skip some datasets in formats that it cannot support. In particular,\n\
2609 it will skip datasets of data types that it does not recognize, with\n\
2610 dimensionality > 2, or with names that aren't valid Octave identifiers\n\
2611 See, however, the @samp{-import} option to ameliorate this somewhat.\n"
2612
2613 HAVE_HDF5_HELP_STRING
2614
2615 "\n\
2616 @item -import\n\
2617 Make a stronger attempt to import foreign datasets. Currently, this means\n\
2618 that for HDF5 files, invalid characters in names are converted to @samp{_},\n\
2619 and datasets with dimensionality > 2 are imported as lists of matrices (or\n\
2620 lists of lists of matrices, or ...).\n\
2621 \n\
1742 @end table\n\ 2622 @end table\n\
1743 @end deffn") 2623 @end deffn")
1744 { 2624 {
1745 octave_value_list retval; 2625 octave_value_list retval;
1746 2626
1758 load_save_format format = LS_UNKNOWN; 2638 load_save_format format = LS_UNKNOWN;
1759 2639
1760 bool force = false; 2640 bool force = false;
1761 bool list_only = false; 2641 bool list_only = false;
1762 bool verbose = false; 2642 bool verbose = false;
2643 bool import = false;
1763 2644
1764 int i; 2645 int i;
1765 for (i = 1; i < argc; i++) 2646 for (i = 1; i < argc; i++)
1766 { 2647 {
1767 if (argv[i] == "-force" || argv[i] == "-f") 2648 if (argv[i] == "-force" || argv[i] == "-f")
1786 } 2667 }
1787 else if (argv[i] == "-mat-binary" || argv[i] == "-m") 2668 else if (argv[i] == "-mat-binary" || argv[i] == "-m")
1788 { 2669 {
1789 format = LS_MAT_BINARY; 2670 format = LS_MAT_BINARY;
1790 } 2671 }
2672 else if (argv[i] == "-hdf5" || argv[i] == "-h")
2673 {
2674 #ifdef HAVE_HDF5
2675 format = LS_HDF5;
2676 #else /* ! HAVE_HDF5 */
2677 error ("load: octave executable was not linked with HDF5 library");
2678 return retval;
2679 #endif /* ! HAVE_HDF5 */
2680 }
2681 else if (argv[i] == "-import" || argv[i] == "-i")
2682 {
2683 import = true;
2684 }
1791 else 2685 else
1792 break; 2686 break;
1793 } 2687 }
1794 2688
1795 if (i == argc) 2689 if (i == argc)
1806 2700
1807 if (argv[i] == "-") 2701 if (argv[i] == "-")
1808 { 2702 {
1809 i++; 2703 i++;
1810 2704
2705 #ifdef HAVE_HDF5
2706 if (format == LS_HDF5)
2707 error ("load: cannot read HDF5 format from stdin");
2708 else
2709 #endif /* HAVE_HDF5 */
1811 if (format != LS_UNKNOWN) 2710 if (format != LS_UNKNOWN)
1812 { 2711 {
1813 // XXX FIXME XXX -- if we have already seen EOF on a 2712 // XXX FIXME XXX -- if we have already seen EOF on a
1814 // previous call, how do we fix up the state of std::cin so 2713 // previous call, how do we fix up the state of std::cin so
1815 // that we can get additional input? I'm afraid that we 2714 // that we can get additional input? I'm afraid that we
1816 // can't fix this using std::cin only. 2715 // can't fix this using std::cin only.
1817 2716
1818 retval = do_load (std::cin, orig_fname, force, format, flt_fmt, 2717 retval = do_load (std::cin, orig_fname, force, format, flt_fmt,
1819 list_only, swap, verbose, argv, i, argc, 2718 list_only, swap, verbose, import, argv, i, argc,
1820 nargout); 2719 nargout);
1821 } 2720 }
1822 else 2721 else
1823 error ("load: must specify file format if reading from stdin"); 2722 error ("load: must specify file format if reading from stdin");
1824 } 2723 }
1827 std::string fname = file_ops::tilde_expand (argv[i]); 2726 std::string fname = file_ops::tilde_expand (argv[i]);
1828 2727
1829 if (format == LS_UNKNOWN) 2728 if (format == LS_UNKNOWN)
1830 format = get_file_format (fname, orig_fname); 2729 format = get_file_format (fname, orig_fname);
1831 2730
2731 #ifdef HAVE_HDF5
2732 if (format == LS_HDF5)
2733 {
2734 i++;
2735
2736 hdf5_ifstream hdf5_file (fname.c_str ());
2737
2738 if (hdf5_file.file_id >= 0)
2739 {
2740 retval = do_load (hdf5_file, orig_fname, force, format,
2741 flt_fmt, list_only, swap, verbose,
2742 import, argv, i, argc, nargout);
2743
2744 hdf5_file.close ();
2745 }
2746 else
2747 error ("load: couldn't open input file `%s'",
2748 orig_fname.c_str ());
2749 }
2750 else
2751 #endif /* HAVE_HDF5 */
2752 // don't insert any statements here; the "else" above has to
2753 // go with the "if" below!!!!!
1832 if (format != LS_UNKNOWN) 2754 if (format != LS_UNKNOWN)
1833 { 2755 {
1834 i++; 2756 i++;
1835 2757
1836 unsigned mode = std::ios::in; 2758 unsigned mode = std::ios::in;
1849 return retval; 2771 return retval;
1850 } 2772 }
1851 } 2773 }
1852 2774
1853 retval = do_load (file, orig_fname, force, format, 2775 retval = do_load (file, orig_fname, force, format,
1854 flt_fmt, list_only, swap, verbose, 2776 flt_fmt, list_only, swap, verbose, import,
1855 argv, i, argc, nargout); 2777 argv, i, argc, nargout);
1856
1857 file.close (); 2778 file.close ();
1858 } 2779 }
1859 else 2780 else
1860 error ("load: couldn't open input file `%s'", 2781 error ("load: couldn't open input file `%s'",
1861 orig_fname.c_str ()); 2782 orig_fname.c_str ());
2065 gripe_wrong_type_arg ("save", tc, false); 2986 gripe_wrong_type_arg ("save", tc, false);
2066 2987
2067 return os; 2988 return os;
2068 } 2989 }
2069 2990
2991 #ifdef HAVE_HDF5
2992
2993 // Add an attribute named attr_name to loc_id (a simple scalar
2994 // attribute with value 1). Return value is >= 0 on success.
2995 static herr_t
2996 hdf5_add_attr (hid_t loc_id, const char *attr_name)
2997 {
2998 herr_t retval = 0;
2999
3000 hid_t as_id = H5Screate (H5S_SCALAR);
3001
3002 if (as_id >= 0)
3003 {
3004 hid_t a_id = H5Acreate (loc_id, attr_name,
3005 H5T_NATIVE_UCHAR, as_id, H5P_DEFAULT);
3006
3007 if (a_id >= 0)
3008 {
3009 unsigned char attr_val = 1;
3010
3011 retval = H5Awrite (a_id, H5T_NATIVE_UCHAR, (void*) &attr_val);
3012
3013 H5Aclose (a_id);
3014 }
3015 else
3016 retval = a_id;
3017
3018 H5Sclose (as_id);
3019 }
3020 else
3021 retval = as_id;
3022
3023 return retval;
3024 }
3025
3026
3027 // save_type_to_hdf5 is not currently used, since hdf5 doesn't yet support
3028 // automatic float<->integer conversions:
3029
3030 #if HAVE_HDF5_INT2FLOAT_CONVERSIONS
3031
3032 // return the HDF5 type id corresponding to the Octave save_type
3033
3034 static hid_t
3035 save_type_to_hdf5 (save_type st)
3036 {
3037 switch (st)
3038 {
3039 case LS_U_CHAR:
3040 return H5T_NATIVE_UCHAR;
3041
3042 case LS_U_SHORT:
3043 return H5T_NATIVE_USHORT;
3044
3045 case LS_U_INT:
3046 return H5T_NATIVE_UINT;
3047
3048 case LS_CHAR:
3049 return H5T_NATIVE_CHAR;
3050
3051 case LS_SHORT:
3052 return H5T_NATIVE_SHORT;
3053
3054 case LS_INT:
3055 return H5T_NATIVE_INT;
3056
3057 case LS_FLOAT:
3058 return H5T_NATIVE_FLOAT;
3059
3060 case LS_DOUBLE:
3061 default:
3062 return H5T_NATIVE_DOUBLE;
3063 }
3064 }
3065 #endif /* HAVE_HDF5_INT2FLOAT_CONVERSIONS */
3066
3067 // Add the data from TC to the HDF5 location loc_id, which could
3068 // be either a file or a group within a file. Return true if
3069 // successful. This function calls itself recursively for lists
3070 // (stored as HDF5 groups).
3071
3072 static bool
3073 add_hdf5_data (hid_t loc_id, const octave_value& tc,
3074 const std::string& name, const std::string& doc,
3075 bool mark_as_global, bool save_as_floats)
3076 {
3077 hsize_t dims[3];
3078 hid_t type_id = -1, space_id = -1, data_id = -1;
3079 bool data_is_group = 0;
3080 bool retval = 0;
3081
3082 if (tc.is_string ())
3083 {
3084 int nr = tc.rows ();
3085 charMatrix chm = tc.char_matrix_value ();
3086 int nc = chm.cols ();
3087
3088 // create datatype for (null-terminated) string to write from:
3089 type_id = H5Tcopy (H5T_C_S1); H5Tset_size (type_id, nc + 1);
3090 if (type_id < 0)
3091 goto error_cleanup;
3092
3093 dims[0] = nr;
3094 space_id = H5Screate_simple (nr > 0 ? 1 : 0, dims, (hsize_t*) NULL);
3095 if (space_id < 0)
3096 goto error_cleanup;
3097
3098 data_id = H5Dcreate (loc_id, name.c_str (),
3099 type_id, space_id, H5P_DEFAULT);
3100 if (data_id < 0)
3101 goto error_cleanup;
3102
3103 char *s = new char [nr * (nc + 1)];
3104
3105 for (int i = 0; i < nr; ++i)
3106 {
3107 std::string tstr = chm.row_as_string (i);
3108 strcpy (s + i * (nc+1), tstr.c_str ());
3109 }
3110
3111 if (H5Dwrite (data_id, type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT,
3112 (void*) s) < 0) {
3113 delete [] s;
3114 goto error_cleanup;
3115 }
3116
3117 delete [] s;
3118 }
3119 else if (tc.is_range ())
3120 {
3121 space_id = H5Screate_simple (0, dims, (hsize_t*) NULL);
3122 if (space_id < 0)
3123 goto error_cleanup;
3124
3125 type_id = hdf5_make_range_type (H5T_NATIVE_DOUBLE);
3126 if (type_id < 0)
3127 goto error_cleanup;
3128
3129 data_id = H5Dcreate (loc_id, name.c_str (),
3130 type_id, space_id, H5P_DEFAULT);
3131 if (data_id < 0)
3132 goto error_cleanup;
3133
3134 Range r = tc.range_value ();
3135 double range_vals[3];
3136 range_vals[0] = r.base ();
3137 range_vals[1] = r.limit ();
3138 range_vals[2] = r.inc ();
3139
3140 if (H5Dwrite (data_id, type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT,
3141 (void*) range_vals) < 0)
3142 goto error_cleanup;
3143 }
3144 else if (tc.is_real_scalar ())
3145 {
3146 space_id = H5Screate_simple (0, dims, (hsize_t*) NULL);
3147 if (space_id < 0) goto error_cleanup;
3148
3149 data_id = H5Dcreate (loc_id, name.c_str (),
3150 H5T_NATIVE_DOUBLE, space_id, H5P_DEFAULT);
3151 if (data_id < 0)
3152 goto error_cleanup;
3153
3154 double tmp = tc.double_value ();
3155 if (H5Dwrite (data_id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL,
3156 H5P_DEFAULT, (void*) &tmp) < 0)
3157 goto error_cleanup;
3158 }
3159 else if (tc.is_real_matrix ())
3160 {
3161 Matrix m = tc.matrix_value ();
3162 dims[1] = m.rows (); // Octave uses column-major, while
3163 dims[0] = m.columns (); // HDF5 uses row-major ordering
3164
3165 space_id = H5Screate_simple (dims[1] > 1 ?2:1, dims, (hsize_t*) NULL);
3166 if (space_id < 0)
3167 goto error_cleanup;
3168
3169 hid_t save_type_id = H5T_NATIVE_DOUBLE;
3170
3171 if (save_as_floats)
3172 {
3173 if (m.too_large_for_float ())
3174 {
3175 warning ("save: some values too large to save as floats --");
3176 warning ("save: saving as doubles instead");
3177 }
3178 else
3179 save_type_id = H5T_NATIVE_FLOAT;
3180 }
3181 #if HAVE_HDF5_INT2FLOAT_CONVERSIONS
3182 // hdf5 currently doesn't support float/integer conversions
3183 else
3184 {
3185 double max_val, min_val;
3186
3187 if (m.all_integers (max_val, min_val))
3188 save_type_id
3189 = save_type_to_hdf5 (get_save_type (max_val, min_val));
3190 }
3191 #endif /* HAVE_HDF5_INT2FLOAT_CONVERSIONS */
3192
3193 data_id = H5Dcreate (loc_id, name.c_str (),
3194 save_type_id, space_id, H5P_DEFAULT);
3195 if (data_id < 0)
3196 goto error_cleanup;
3197
3198 double *mtmp = m.fortran_vec ();
3199 if (H5Dwrite (data_id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL,
3200 H5P_DEFAULT, (void*) mtmp) < 0)
3201 goto error_cleanup;
3202 }
3203 else if (tc.is_complex_scalar ())
3204 {
3205 space_id = H5Screate_simple (0, dims, (hsize_t*) NULL);
3206 if (space_id < 0)
3207 goto error_cleanup;
3208
3209 type_id = hdf5_make_complex_type (H5T_NATIVE_DOUBLE);
3210 if (type_id < 0)
3211 goto error_cleanup;
3212
3213 data_id = H5Dcreate (loc_id, name.c_str (),
3214 type_id, space_id, H5P_DEFAULT);
3215 if (data_id < 0)
3216 goto error_cleanup;
3217
3218 Complex tmp = tc.complex_value ();
3219 if (H5Dwrite (data_id, type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT,
3220 (void*) X_CAST (double*, &tmp)) < 0)
3221 goto error_cleanup;
3222 }
3223 else if (tc.is_complex_matrix ())
3224 {
3225 ComplexMatrix m = tc.complex_matrix_value ();
3226
3227 dims[1] = m.rows (); // Octave uses column-major, while
3228 dims[0] = m.columns (); // HDF5 uses row-major ordering
3229
3230 space_id = H5Screate_simple (dims[1] > 1 ?2:1, dims, (hsize_t*) NULL);
3231 if (space_id < 0)
3232 goto error_cleanup;
3233
3234 hid_t save_type_id = H5T_NATIVE_DOUBLE;
3235
3236 if (save_as_floats)
3237 {
3238 if (m.too_large_for_float ())
3239 {
3240 warning ("save: some values too large to save as floats --");
3241 warning ("save: saving as doubles instead");
3242 }
3243 else
3244 save_type_id = H5T_NATIVE_FLOAT;
3245 }
3246 #if HAVE_HDF5_INT2FLOAT_CONVERSIONS
3247 // hdf5 currently doesn't support float/integer conversions
3248 else
3249 {
3250 double max_val, min_val;
3251
3252 if (m.all_integers (max_val, min_val))
3253 save_type_id
3254 = save_type_to_hdf5 (get_save_type (max_val, min_val));
3255 }
3256 #endif /* HAVE_HDF5_INT2FLOAT_CONVERSIONS */
3257
3258 type_id = hdf5_make_complex_type (save_type_id);
3259 if (type_id < 0) goto error_cleanup;
3260
3261 data_id = H5Dcreate (loc_id, name.c_str (),
3262 type_id, space_id, H5P_DEFAULT);
3263 if (data_id < 0)
3264 goto error_cleanup;
3265
3266 hid_t complex_type_id = hdf5_make_complex_type (H5T_NATIVE_DOUBLE);
3267 if (complex_type_id < 0)
3268 goto error_cleanup;
3269
3270 Complex *mtmp = m.fortran_vec ();
3271 if (H5Dwrite (data_id, complex_type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT,
3272 (void*) X_CAST (double *, mtmp)) < 0)
3273 {
3274 H5Tclose (complex_type_id);
3275 goto error_cleanup;
3276 }
3277
3278 H5Tclose (complex_type_id);
3279 }
3280 else if (tc.is_list ())
3281 {
3282 data_id = H5Gcreate (loc_id, name.c_str (), 0);
3283 if (data_id < 0)
3284 goto error_cleanup;
3285
3286 data_is_group = 1;
3287
3288 // recursively add each element of the list to this group
3289 octave_value_list lst = tc.list_value ();
3290
3291 for (int i = 0; i < lst.length (); ++i)
3292 {
3293 // should we use lst.name_tags () to label the elements?
3294 char s[20];
3295 sprintf (s, "%d", i);
3296 bool retval2 = add_hdf5_data (data_id, lst (i), s, "",
3297 false, save_as_floats);
3298 if (! retval2)
3299 goto error_cleanup;
3300 }
3301
3302 // mark with an attribute "OCTAVE_LIST" with value 1
3303 // to distinguish from structures (also stored as HDF5 groups):
3304 if (hdf5_add_attr(data_id, "OCTAVE_LIST") < 0)
3305 goto error_cleanup;
3306 }
3307 else if (tc.is_map ())
3308 {
3309 // an Octave structure
3310 data_id = H5Gcreate (loc_id, name.c_str (), 0);
3311 if (data_id < 0)
3312 goto error_cleanup;
3313
3314 data_is_group = 1;
3315
3316 // recursively add each element of the structure to this group
3317 Octave_map m = tc.map_value ();
3318 Pix i = m.first ();
3319 while (i)
3320 {
3321 bool retval2 = add_hdf5_data (data_id,
3322 m.contents (i), m.key (i), "",
3323 false, save_as_floats);
3324 if (! retval2)
3325 goto error_cleanup;
3326
3327 // advance i to next element, or 0
3328 m.next (i);
3329 }
3330 }
3331 else
3332 {
3333 gripe_wrong_type_arg ("save", tc, false);
3334 goto error_cleanup;
3335 }
3336
3337 // attach doc string as comment:
3338 if (doc.length () > 0
3339 && H5Gset_comment (loc_id, name.c_str (), doc.c_str ()) < 0)
3340 goto error_cleanup;
3341
3342 retval = 1;
3343
3344 // if it's global, add an attribute "OCTAVE_GLOBAL" with value 1
3345 if (mark_as_global)
3346 retval = hdf5_add_attr(data_id, "OCTAVE_GLOBAL") >= 0;
3347
3348 error_cleanup:
3349
3350 if (! retval)
3351 error ("save: error while writing `%s' to hdf5 file", name.c_str ());
3352
3353 if (data_id >= 0)
3354 {
3355 if (data_is_group)
3356 H5Gclose (data_id);
3357 else
3358 H5Dclose (data_id);
3359 }
3360
3361 if (space_id >= 0)
3362 H5Sclose (space_id);
3363
3364 if (type_id >= 0)
3365 H5Tclose (type_id);
3366
3367 return retval;
3368 }
3369
3370 // Write data from TC in HDF5 (binary) format to the stream OS,
3371 // which must be an hdf5_ofstream, returning true on success.
3372
3373 static bool
3374 save_hdf5_data (std::ostream& os, const octave_value& tc,
3375 const std::string& name, const std::string& doc,
3376 bool mark_as_global, bool save_as_floats)
3377 {
3378 hdf5_ofstream& hs = (hdf5_ofstream&) os;
3379
3380 return add_hdf5_data (hs.file_id, tc, name, doc,
3381 mark_as_global, save_as_floats);
3382 }
3383
3384 #endif /* HAVE_HDF5 */
3385
2070 // Save the data from TC along with the corresponding NAME on stream OS 3386 // Save the data from TC along with the corresponding NAME on stream OS
2071 // in the MatLab binary format. 3387 // in the MatLab binary format.
2072 3388
2073 static bool 3389 static bool
2074 save_mat_binary_data (std::ostream& os, const octave_value& tc, 3390 save_mat_binary_data (std::ostream& os, const octave_value& tc,
2405 3721
2406 case LS_MAT_BINARY: 3722 case LS_MAT_BINARY:
2407 save_mat_binary_data (os, tc, name); 3723 save_mat_binary_data (os, tc, name);
2408 break; 3724 break;
2409 3725
3726 #ifdef HAVE_HDF5
3727 case LS_HDF5:
3728 save_hdf5_data (os, tc, name, help, global, save_as_floats);
3729 break;
3730 #endif /* HAVE_HDF5 */
3731
2410 default: 3732 default:
2411 gripe_unrecognized_data_fmt ("save"); 3733 gripe_unrecognized_data_fmt ("save");
2412 break; 3734 break;
2413 } 3735 }
2414 } 3736 }
2426 3748
2427 int saved = vars.length (); 3749 int saved = vars.length ();
2428 3750
2429 for (int i = 0; i < saved; i++) 3751 for (int i = 0; i < saved; i++)
2430 { 3752 {
2431 do_save (os, vars(i), fmt, save_as_floats); 3753 do_save (os, vars (i), fmt, save_as_floats);
2432 3754
2433 if (error_state) 3755 if (error_state)
2434 break; 3756 break;
2435 } 3757 }
2436 3758
2443 3765
2444 saved += count; 3766 saved += count;
2445 3767
2446 for (int i = 0; i < count; i++) 3768 for (int i = 0; i < count; i++)
2447 { 3769 {
2448 do_save (os, vars(i), fmt, save_as_floats); 3770 do_save (os, vars (i), fmt, save_as_floats);
2449 3771
2450 if (error_state) 3772 if (error_state)
2451 break; 3773 break;
2452 } 3774 }
2453 } 3775 }
2464 3786
2465 if (fmt == "binary") 3787 if (fmt == "binary")
2466 retval = LS_BINARY; 3788 retval = LS_BINARY;
2467 else if (fmt == "mat-binary" || fmt =="mat_binary") 3789 else if (fmt == "mat-binary" || fmt =="mat_binary")
2468 retval = LS_MAT_BINARY; 3790 retval = LS_MAT_BINARY;
3791 #ifdef HAVE_HDF5
3792 else if (fmt == "hdf5")
3793 retval = LS_HDF5;
3794 #endif /* HAVE_HDF5 */
2469 3795
2470 return retval; 3796 return retval;
2471 } 3797 }
2472 3798
2473 static void 3799 static void
2487 3813
2488 os.write (X_CAST (char *, &tmp), 1); 3814 os.write (X_CAST (char *, &tmp), 1);
2489 } 3815 }
2490 break; 3816 break;
2491 3817
3818 #ifdef HAVE_HDF5
3819 case LS_HDF5:
3820 #endif /* HAVE_HDF5 */
2492 case LS_ASCII: 3821 case LS_ASCII:
2493 { 3822 {
2494 octave_gmtime now; 3823 octave_gmtime now;
2495 std::string time_string = now.asctime (); 3824 std::string time_string = now.asctime ();
2496 time_string = time_string.substr (0, time_string.length () - 1); 3825 time_string = time_string.substr (0, time_string.length () - 1);
2497 3826 std::ostream *s = &os;
2498 os << "# Created by Octave " OCTAVE_VERSION ", " 3827 #ifdef HAVE_HDF5
3828 // for HDF5, write data to a string instead of to os,
3829 // and then save the string as the HDF5 file's "comment" field.
3830 std::ostrstream ss;
3831 if (format == LS_HDF5)
3832 s = &ss;
3833 #endif /* HAVE_HDF5 */
3834
3835 *s << "# Created by Octave " OCTAVE_VERSION ", "
2499 << time_string 3836 << time_string
2500 << " <" 3837 << " <"
2501 << octave_env::get_user_name () 3838 << octave_env::get_user_name ()
2502 << "@" 3839 << "@"
2503 << octave_env::get_host_name () 3840 << octave_env::get_host_name ()
2504 << ">" << "\n"; 3841 << ">";
3842
3843 #ifdef HAVE_HDF5
3844 if (format != LS_HDF5) // don't append newline for HDF5
3845 #endif /* HAVE_HDF5 */
3846 *s << "\n";
3847
3848 #ifdef HAVE_HDF5
3849 if (format == LS_HDF5)
3850 {
3851 hdf5_ofstream& hs = (hdf5_ofstream&) os;
3852 H5Gset_comment (hs.file_id, "/", ss.str ());
3853 }
3854 #endif /* HAVE_HDF5 */
2505 } 3855 }
2506 break; 3856 break;
2507 3857
2508 default: 3858 default:
2509 break; 3859 break;
2510 } 3860 }
2511 } 3861 }
2512 3862
2513 static void 3863 static void
2514 save_vars (const string_vector& argv, int argv_idx, int argc, 3864 save_vars (const std::string_vector& argv, int argv_idx, int argc,
2515 std::ostream& os, bool save_builtins, load_save_format fmt, 3865 std::ostream& os, bool save_builtins, load_save_format fmt,
2516 bool save_as_floats, bool write_header_info) 3866 bool save_as_floats, bool write_header_info)
2517 { 3867 {
2518 if (write_header_info) 3868 if (write_header_info)
2519 write_header (os, fmt); 3869 write_header (os, fmt);
2549 3899
2550 unsigned mode = std::ios::out|std::ios::trunc; 3900 unsigned mode = std::ios::out|std::ios::trunc;
2551 if (format == LS_BINARY || format == LS_MAT_BINARY) 3901 if (format == LS_BINARY || format == LS_MAT_BINARY)
2552 mode |= std::ios::binary; 3902 mode |= std::ios::binary;
2553 3903
2554 std::ofstream file (fname, mode); 3904 #ifdef HAVE_HDF5
2555 3905 if (format == LS_HDF5)
2556 if (file) 3906 {
2557 { 3907 hdf5_ofstream file (fname);
2558 save_vars (string_vector (), 0, 0, file, false, format, false, true); 3908
2559 message (0, "save to `%s' complete", fname); 3909 if (file.file_id >= 0)
3910 {
3911 save_vars (string_vector (), 0, 0, file,
3912 false, format, false, true);
3913
3914 message (0, "save to `%s' complete", fname);
3915
3916 file.close ();
3917 }
3918 else
3919 warning ("unable to open `%s' for writing...", fname);
2560 } 3920 }
2561 else 3921 else
2562 warning ("unable to open `%s' for writing...", fname); 3922 #endif /* HAVE_HDF5 */
3923 // don't insert any commands here! The open brace below must
3924 // go with the else above!
3925 {
3926 std::ofstream file (fname, mode);
3927
3928 if (file)
3929 {
3930 save_vars (string_vector (), 0, 0, file,
3931 false, format, false, true);
3932 message (0, "save to `%s' complete", fname);
3933 file.close ();
3934 }
3935 else
3936 warning ("unable to open `%s' for writing...", fname);
3937 }
2563 } 3938 }
2564 } 3939 }
2565 3940
2566 DEFUN_TEXT (save, args, , 3941 DEFUN_TEXT (save, args, ,
2567 "-*- texinfo -*-\n\ 3942 "-*- texinfo -*-\n\
2586 precision. You should use this format only if you know that all the\n\ 3961 precision. You should use this format only if you know that all the\n\
2587 values to be saved can be represented in single precision.\n\ 3962 values to be saved can be represented in single precision.\n\
2588 \n\ 3963 \n\
2589 @item -mat-binary\n\ 3964 @item -mat-binary\n\
2590 Save the data in @sc{Matlab}'s binary data format.\n\ 3965 Save the data in @sc{Matlab}'s binary data format.\n\
3966 \n\
3967 @item -hdf5\n\
3968 Save the data in HDF5 format.\n\
3969 (HDF5 is a free, portable binary format developed by the National\n\
3970 Center for Supercomputing Applications at the University of Illinois.)\n"
3971
3972 HAVE_HDF5_HELP_STRING
3973
3974 "\n\
3975 @item -float-hdf5\n\
3976 Save the data in HDF5 format but only using single precision.\n\
3977 You should use this format only if you know that all the\n\
3978 values to be saved can be represented in single precision.\n\
2591 \n\ 3979 \n\
2592 @item -save-builtins\n\ 3980 @item -save-builtins\n\
2593 Force Octave to save the values of built-in variables too. By default,\n\ 3981 Force Octave to save the values of built-in variables too. By default,\n\
2594 Octave does not save built-in variables.\n\ 3982 Octave does not save built-in variables.\n\
2595 @end table\n\ 3983 @end table\n\
2659 } 4047 }
2660 else if (argv[i] == "-binary" || argv[i] == "-b") 4048 else if (argv[i] == "-binary" || argv[i] == "-b")
2661 { 4049 {
2662 format = LS_BINARY; 4050 format = LS_BINARY;
2663 } 4051 }
4052 else if (argv[i] == "-hdf5" || argv[i] == "-h")
4053 {
4054 #ifdef HAVE_HDF5
4055 format = LS_HDF5;
4056 #else /* ! HAVE_HDF5 */
4057 error ("save: octave executable was not linked with HDF5 library");
4058 return retval;
4059 #endif /* ! HAVE_HDF5 */
4060 }
2664 else if (argv[i] == "-mat-binary" || argv[i] == "-m") 4061 else if (argv[i] == "-mat-binary" || argv[i] == "-m")
2665 { 4062 {
2666 format = LS_MAT_BINARY; 4063 format = LS_MAT_BINARY;
2667 } 4064 }
2668 else if (argv[i] == "-float-binary" || argv[i] == "-f") 4065 else if (argv[i] == "-float-binary" || argv[i] == "-f")
2669 { 4066 {
2670 format = LS_BINARY; 4067 format = LS_BINARY;
2671 save_as_floats = true; 4068 save_as_floats = true;
2672 } 4069 }
4070 else if (argv[i] == "-float-hdf5")
4071 {
4072 #ifdef HAVE_HDF5
4073 format = LS_HDF5;
4074 save_as_floats = true;
4075 #else /* ! HAVE_HDF5 */
4076 error ("save: octave executable was not linked with HDF5 library");
4077 return retval;
4078 #endif /* ! HAVE_HDF5 */
4079 }
2673 else if (argv[i] == "-save-builtins") 4080 else if (argv[i] == "-save-builtins")
2674 { 4081 {
2675 save_builtins = true; 4082 save_builtins = true;
2676 } 4083 }
2677 else 4084 else
2692 4099
2693 if (argv[i] == "-") 4100 if (argv[i] == "-")
2694 { 4101 {
2695 i++; 4102 i++;
2696 4103
2697 // XXX FIXME XXX -- should things intended for the screen end up 4104 #ifdef HAVE_HDF5
2698 // in a octave_value (string)? 4105 if (format == LS_HDF5)
2699 4106 error ("load: cannot write HDF5 format to stdout");
2700 save_vars (argv, i, argc, octave_stdout, save_builtins, format, 4107 else
2701 save_as_floats, true); 4108 #endif /* HAVE_HDF5 */
4109 // don't insert any commands here! the brace below must go
4110 // with the "else" above!
4111 {
4112 // XXX FIXME XXX -- should things intended for the screen end up
4113 // in a octave_value (string)?
4114
4115 save_vars (argv, i, argc, octave_stdout, save_builtins, format,
4116 save_as_floats, true);
4117 }
2702 } 4118 }
2703 4119
2704 // Guard against things like `save a*', which are probably mistakes... 4120 // Guard against things like `save a*', which are probably mistakes...
2705 4121
2706 else if (i == argc - 1 && glob_pattern_p (argv[i])) 4122 else if (i == argc - 1 && glob_pattern_p (argv[i]))
2718 if (format == LS_BINARY || format == LS_MAT_BINARY) 4134 if (format == LS_BINARY || format == LS_MAT_BINARY)
2719 mode |= std::ios::binary; 4135 mode |= std::ios::binary;
2720 4136
2721 mode |= append ? std::ios::ate : std::ios::trunc; 4137 mode |= append ? std::ios::ate : std::ios::trunc;
2722 4138
2723 std::ofstream file (fname.c_str (), mode); 4139 #ifdef HAVE_HDF5
2724 4140 if (format == LS_HDF5)
2725 if (file) 4141 {
2726 { 4142 hdf5_ofstream hdf5_file (fname.c_str ());
2727 bool write_header_info 4143
2728 = ((file.rdbuf ())->pubseekoff (0, std::ios::cur) == 0); 4144 if (hdf5_file.file_id >= 0) {
2729 4145 save_vars (argv, i, argc, hdf5_file, save_builtins, format,
2730 save_vars (argv, i, argc, file, save_builtins, format, 4146 save_as_floats, true);
2731 save_as_floats, write_header_info); 4147
4148 hdf5_file.close ();
4149 }
4150 else
4151 {
4152 error ("save: couldn't open output file `%s'", fname.c_str ());
4153 return retval;
4154 }
2732 } 4155 }
2733 else 4156 else
2734 { 4157 #endif /* HAVE_HDF5 */
2735 error ("save: couldn't open output file `%s'", fname.c_str ()); 4158 // don't insert any statements here! The brace below must go
2736 return retval; 4159 // with the "else" above!
4160 {
4161 std::ofstream file (fname.c_str (), mode);
4162
4163 if (file)
4164 {
4165 bool write_header_info
4166 = ( (file.rdbuf ())->seekoff (0, std::ios::cur) == 0);
4167
4168 save_vars (argv, i, argc, file, save_builtins, format,
4169 save_as_floats, write_header_info);
4170 }
4171 else
4172 {
4173 error ("save: couldn't open output file `%s'", fname.c_str ());
4174 return retval;
4175 }
2737 } 4176 }
2738 } 4177 }
2739 4178
2740 return retval; 4179 return retval;
2741 } 4180 }