Mercurial > hg > octave-lyh
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, ¤t_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 } |