# HG changeset patch # User jwe # Date 962319025 0 # Node ID 8aea513ff224fa83c49f34c8bb11e06ca1968c13 # Parent b5a285d1c1f48b22405b3749dc6cb083a6935309 [project @ 2000-06-29 22:50:23 by jwe] diff -r b5a285d1c1f4 -r 8aea513ff224 ChangeLog --- a/ChangeLog Thu Jun 29 21:33:01 2000 +0000 +++ b/ChangeLog Thu Jun 29 22:50:25 2000 +0000 @@ -1,3 +1,7 @@ +2000-06-29 John W. Eaton + + * configure.in: Check for long long data type. + 2000-06-29 Steven G. Johnson * acconfig.h (HAVE_HDF5): Add undef. diff -r b5a285d1c1f4 -r 8aea513ff224 configure.in --- a/configure.in Thu Jun 29 21:33:01 2000 +0000 +++ b/configure.in Thu Jun 29 22:50:25 2000 +0000 @@ -21,7 +21,7 @@ ### Software Foundation, 59 Temple Place - Suite 330, Boston, MA ### 02111-1307, USA. -AC_REVISION($Revision: 1.328 $) +AC_REVISION($Revision: 1.329 $) AC_PREREQ(2.9) AC_INIT(src/octave.cc) AC_CONFIG_HEADER(config.h) @@ -708,6 +708,7 @@ AC_CHECK_SIZEOF(short, 2) AC_CHECK_SIZEOF(int, 4) AC_CHECK_SIZEOF(long, 4) +AC_CHECK_SIZEOF(long long, 8) ### Does the C compiler handle alloca() and const correctly? diff -r b5a285d1c1f4 -r 8aea513ff224 liboctave/ChangeLog --- a/liboctave/ChangeLog Thu Jun 29 21:33:01 2000 +0000 +++ b/liboctave/ChangeLog Thu Jun 29 22:50:25 2000 +0000 @@ -1,3 +1,10 @@ +2000-06-29 James R. Van Zandt + + * data-conv.cc (read_doubles): Handle EIGHT_BYTE_INT cases. + (write_doubles): Ditto. + * data-conv.h: Ditto. + (enum save_type): New values, LS_U_LONG and LS_LONG. + 2000-06-27 John W. Eaton * boolMatrix.h: Declare MM_CMP_OPS here. diff -r b5a285d1c1f4 -r 8aea513ff224 liboctave/data-conv.h --- a/liboctave/data-conv.h Thu Jun 29 21:33:01 2000 +0000 +++ b/liboctave/data-conv.h Thu Jun 29 22:50:25 2000 +0000 @@ -49,6 +49,16 @@ #error "No 4 byte integer type found!" #endif +#if SIZEOF_LONG == 8 +#define EIGHT_BYTE_INT long +#else +#if SIZEOF_LONG_LONG == 8 +// if `long long' is not implemented, then SIZEOF_LONG_LONG will be 0 +#define EIGHT_BYTE_INT long long +// if no 8 byte integer type is found, then EIGHT_BYTE_INT is not defined +#endif +#endif + class oct_data_conv { @@ -80,9 +90,15 @@ LS_U_CHAR, LS_U_SHORT, LS_U_INT, +#ifdef EIGHT_BYTE_INT + LS_U_LONG, +#endif LS_CHAR, LS_SHORT, LS_INT, +#ifdef EIGHT_BYTE_INT + LS_LONG, +#endif LS_FLOAT, LS_DOUBLE }; diff -r b5a285d1c1f4 -r 8aea513ff224 src/ChangeLog --- a/src/ChangeLog Thu Jun 29 21:33:01 2000 +0000 +++ b/src/ChangeLog Thu Jun 29 22:50:25 2000 +0000 @@ -1,3 +1,25 @@ +2000-06-29 James R. Van Zandt + + * load-save.cc (read_int): New template function. Instantiate it + for various integer types. + (load_save_format): New value, LS_MAT5_BINARY. + (arrayclasstype, mat5_data_type): New enums. + (read_mat5_binary_data): New function. + (read_mat5_tag): New function. + (read_mat5_binary_element): New function. + (read_mat5_binary_file_header): New function. + (get_file_format): Check for mat5 binary format too. + (do_load): Handle mat5 binary format. + (write_mat5_tag): New function. + (write_mat5_array): New function. + (class mat5_callback): New class. + (save_mat5_binary_element): New functnon. + (do_save): Handle mat5 binary format. + (write_header): Handle LS_MAT5_BINARY case too. + (save_user_variables): Handle LS_MAT5_BINARY case too. + (Fsave): Handle LS_MAT5_BINARY case too. + (Fload): Handle LS_MAT5_BINARY case too. + 2000-06-29 Steven G. Johnson All of the following changes are protected by #ifdef HAVE_HDF5. diff -r b5a285d1c1f4 -r 8aea513ff224 src/load-save.cc --- a/src/load-save.cc Thu Jun 29 21:33:01 2000 +0000 +++ b/src/load-save.cc Thu Jun 29 22:50:25 2000 +0000 @@ -65,6 +65,52 @@ #include "utils.h" #include "variables.h" #include "version.h" +#include "dMatrix.h" + +#define PAD(l) (((l)<=4)?4:(((l)+7)/8)*8) +#define TAGLENGTH(l) ((l)<=4?4:8) + +template +void +read_int (std::istream& is, bool swap_bytes, T& val) +{ + is.read (X_CAST (char *, &val), sizeof (T)); + + if (swap_bytes) + { + switch (sizeof (T)) + { + case 1: + break; + + case 2: + swap_2_bytes (X_CAST (char *, &val)); + break; + + case 4: + swap_4_bytes (X_CAST (char *, &val)); + break; + + case 8: + swap_8_bytes (X_CAST (char *, &val)); + break; + + default: + (*current_liboctave_error_handler) + ("read_int: unrecognized data format!"); + } + } +} + +template void read_int (std::istream&, bool, char&); +template void read_int (std::istream&, bool, signed char&); +template void read_int (std::istream&, bool, unsigned char&); +template void read_int (std::istream&, bool, short&); +template void read_int (std::istream&, bool, unsigned short&); +template void read_int (std::istream&, bool, int&); +template void read_int (std::istream&, bool, unsigned int&); +template void read_int (std::istream&, bool, long&); +template void read_int (std::istream&, bool, unsigned long&); // Write octave-core file if Octave crashes or is killed by a signal. static bool Vcrash_dumps_octave_core; @@ -88,12 +134,48 @@ LS_BINARY, LS_MAT_ASCII, LS_MAT_BINARY, + LS_MAT5_BINARY, #ifdef HAVE_HDF5 LS_HDF5, #endif /* HAVE_HDF5 */ LS_UNKNOWN }; + enum arrayclasstype + { + mxCELL_CLASS=1, // cell array + mxSTRUCT_CLASS, // structure + mxOBJECT_CLASS, // object + mxCHAR_CLASS, // character array + mxSPARSE_CLASS, // sparse array + mxDOUBLE_CLASS, // double precision array + mxSINGLE_CLASS, // single precision floating point + mxINT8_CLASS, // 8 bit signed integer + mxUINT8_CLASS, // 8 bit unsigned integer + mxINT16_CLASS, // 16 bit signed integer + mxUINT16_CLASS, // 16 bit unsigned integer + mxINT32_CLASS, // 32 bit signed integer + mxUINT32_CLASS // 32 bit unsigned integer + }; + +enum mat5_data_type + { + miINT8=1, // 8 bit signed + miUINT8, // 8 bit unsigned + miINT16, // 16 bit signed + miUINT16, // 16 bit unsigned + miINT32, // 32 bit signed + miUINT32, // 32 bit unsigned + miSINGLE, // IEEE 754 single precision float + miRESERVE1, + miDOUBLE, // IEEE 754 double precision float + miRESERVE2, + miRESERVE3, + miINT64, // 64 bit signed + miUINT64, // 64 bit unsigned + miMATRIX // MATLAB array + }; + // Return TRUE if S is a valid identifier. static bool @@ -1225,7 +1307,7 @@ H5Eget_auto (&err_func, &err_func_data); H5Eset_auto (NULL, NULL); - hid_t attr_id = H5Aopen_name(loc_id, attr_name); + hid_t attr_id = H5Aopen_name (loc_id, attr_name); if (attr_id >= 0) { @@ -1290,7 +1372,7 @@ strcpy (vname, name); - if (!ident_valid && d->import) + if (! ident_valid && d->import) { // fix the identifier, replacing invalid chars with underscores make_valid_identifier (vname); @@ -1600,7 +1682,7 @@ H5Tclose (type_id); // check for OCTAVE_GLOBAL attribute: - d->global = hdf5_check_attr(data_id, "OCTAVE_GLOBAL"); + d->global = hdf5_check_attr (data_id, "OCTAVE_GLOBAL"); H5Dclose (data_id); } @@ -1621,7 +1703,7 @@ // default (since that preserves name information), and an // octave list otherwise. - bool is_list = hdf5_check_attr(subgroup_id, "OCTAVE_LIST"); + bool is_list = hdf5_check_attr (subgroup_id, "OCTAVE_LIST"); hdf5_callback_data dsub; dsub.name = dsub.doc = (char*) NULL; @@ -1655,7 +1737,7 @@ retval = retval2; else { - d->global = hdf5_check_attr(group_id, "OCTAVE_GLOBAL"); + d->global = hdf5_check_attr (group_id, "OCTAVE_GLOBAL"); if (is_list) d->tc = lst; @@ -2157,8 +2239,8 @@ // Extract one value (scalar, matrix, string, etc.) from stream IS and // place it in TC, returning the name of the variable. // -// The data is expected to be in Matlab's .mat format, though not all -// the features of that format are supported. +// The data is expected to be in Matlab version 4 .mat format, though +// not all the features of that format are supported. // // FILENAME is used for error messages. // @@ -2285,6 +2367,420 @@ return 0; } +// Read COUNT elements of data from IS in the format specified by TYPE, +// placing the result in DATA. If SWAP is TRUE, swap the bytes of +// each element before copying to DATA. FLT_FMT specifies the format +// of the data if we are reading floating point numbers. + +static void +read_mat5_binary_data (std::istream& is, double *data, + int count, bool swap, mat5_data_type type, + oct_mach_info::float_format flt_fmt) +{ + + switch (type) + { + case miINT8: + read_doubles (is, data, LS_CHAR, count, swap, flt_fmt); + break; + + case miUINT8: + read_doubles (is, data, LS_U_CHAR, count, swap, flt_fmt); + break; + + case miINT16: + read_doubles (is, data, LS_SHORT, count, swap, flt_fmt); + break; + + case miUINT16: + read_doubles (is, data, LS_U_SHORT, count, swap, flt_fmt); + break; + + case miINT32: + read_doubles (is, data, LS_INT, count, swap, flt_fmt); + break; + + case miUINT32: + read_doubles (is, data, LS_U_INT, count, swap, flt_fmt); + break; + + case miSINGLE: + read_doubles (is, data, LS_FLOAT, count, swap, flt_fmt); + break; + + case miRESERVE1: + break; + + case miDOUBLE: + read_doubles (is, data, LS_DOUBLE, count, swap, flt_fmt); + break; + + case miRESERVE2: + case miRESERVE3: + break; + + case miINT64: +#ifdef EIGHT_BYTE_INT + read_doubles (is, data, LS_LONG, count, swap, flt_fmt); +#endif + break; + + case miUINT64: +#ifdef EIGHT_BYTE_INT + read_doubles (is, data, LS_U_LONG, count, swap, flt_fmt); +#endif + break; + + case miMATRIX: + default: + break; + } +} + +// Read one element tag from stream IS, +// place the type code in TYPE and the byte count in BYTES +// return nonzero on error +static int +read_mat5_tag (std::istream& is, bool swap, int& type, int& bytes) +{ + unsigned int upper; + FOUR_BYTE_INT temp; + + if (! is.read (X_CAST (char *, &temp), 4 )) + goto data_read_error; + + if (swap) + swap_4_bytes ((char *)&temp); + + upper = (temp >> 16) & 0xffff; + type = temp & 0xffff; + + if (upper) + { + // "compressed" format + bytes = upper; + } + else + { + if (! is.read (X_CAST (char *, &temp), 4 )) + goto data_read_error; + if (swap) + swap_4_bytes ((char *)&temp); + bytes = temp; + } + + return 0; + + data_read_error: + return 1; +} + +// Extract one data element (scalar, matrix, string, etc.) from stream +// IS and place it in TC, returning the name of the variable. +// +// The data is expected to be in Matlab's "Version 5" .mat format, +// though not all the features of that format are supported. +// +// FILENAME is used for error messages. + +static char * +read_mat5_binary_element (std::istream& is, const std::string& filename, + bool swap, bool& global, octave_value& tc) +{ + // These are initialized here instead of closer to where they are + // first used to avoid errors from gcc about goto crossing + // initialization of variable. + + Matrix re; + oct_mach_info::float_format flt_fmt = oct_mach_info::unknown; + char *name = 0; + int type = 0; + bool imag; + bool logicalvar; + enum arrayclasstype arrayclass; + FOUR_BYTE_INT junk; + FOUR_BYTE_INT flags; + FOUR_BYTE_INT nr; + FOUR_BYTE_INT nc; + FOUR_BYTE_INT dimension_length; + int len; + int element_length; + std::streampos pos; + TWO_BYTE_INT number; + number = *(TWO_BYTE_INT *)"\x00\x01"; + + // MAT files always use IEEE floating point + if ((number == 1) ^ swap) + flt_fmt = oct_mach_info::ieee_big_endian; + else + flt_fmt = oct_mach_info::ieee_little_endian; + + // element type and length + if (read_mat5_tag (is, swap, type, element_length)) + return 0; // EOF + + if (type != miMATRIX) + { + error ("load: invalid element type"); + goto early_read_error; + } + pos = is.tellg (); + + // array flags subelement + if (read_mat5_tag (is, swap, type, len) || type != miUINT32 || len != 8) + { + error ("load: invalid array flags subelement"); + goto early_read_error; + } + + read_int (is, swap, flags); + imag = (flags & 0x0800) != 0; // has an imaginary part? + global = (flags & 0x0400) != 0; // global variable? + logicalvar = (flags & 0x0200) != 0; // we don't use this yet + arrayclass = (arrayclasstype)(flags & 0xff); + read_int (is, swap, junk); // an "undefined" entry + + // dimensions array subelement + { + std::streampos pos; + + if (read_mat5_tag (is, swap, type, dimension_length) || type != miINT32) + { + error ("load: invalid dimensions array subelement"); + goto early_read_error; + } + + pos = is.tellg (); + read_int (is, swap, nr); + read_int (is, swap, nc); + re.resize (nr, nc); + + // delay checking for a multidimensional array until we have read + // the variable name + is.seekg (pos + dimension_length); + } + + // array name subelement + { + std::streampos pos; + + if (read_mat5_tag (is, swap, type, len) || type != miINT8) + { + error ("load: invalid array name subelement"); + goto early_read_error; + } + + pos = is.tellg (); + name = new char[len+1]; + + if (len) // structure field subelements have + // zero-length array name subelements + { + if (! is.read (X_CAST (char *, name), len )) + goto data_read_error; + + is.seekg (pos + PAD (len)); + } + + name[len] = '\0'; + } + + if (dimension_length != 8) + { + error ("load: multidimension arrays are not implemented"); + goto skip_ahead; + } + + switch (arrayclass) + { + case mxCELL_CLASS: + warning ("load: cell arrays are not implemented"); + goto skip_ahead; + + case mxOBJECT_CLASS: + warning ("load: objects are not implemented"); + goto skip_ahead; + + case mxSPARSE_CLASS: + warning ("load: sparse arrays are not implemented"); + goto skip_ahead; + + case mxSTRUCT_CLASS: + { + Octave_map m; + FOUR_BYTE_INT type; + FOUR_BYTE_INT len; + FOUR_BYTE_INT field_name_length; + int i; + char *elname; + + // field name length subelement -- actually the maximum length + // of a field name. The Matlab docs promise this will always + // be 32. We read and use the actual value, on the theory + // that eventually someone will recognize that's a waste of + // space. + if (read_mat5_tag (is, swap, type, len) || type != miINT32) + { + error ("load: invalid field name subelement"); + goto data_read_error; + } + + if (! is.read (X_CAST (char *, &field_name_length), len )) + goto data_read_error; + + if (swap) + swap_4_bytes ((char *)&field_name_length); + + // field name subelement. The length of this subelement tells + // us how many fields there are. + if (read_mat5_tag (is, swap, type, len) || type != miINT8) + { + error ("load: invalid field name subelement"); + goto data_read_error; + } + + elname = new char[len]; + + if (! is.read (elname, len)) + goto data_read_error; + + // fields subelements + for (i=0; i < len/field_name_length; i++) + { + char *thename; + octave_value fieldtc; + thename = read_mat5_binary_element (is, filename, swap, + global, fieldtc); + m[elname + i*field_name_length] = fieldtc; + delete [] thename; + } + + delete [] elname; + tc = m; + } + break; + + case mxCHAR_CLASS: + // handle as a numerical array to start with + + case mxDOUBLE_CLASS: + case mxSINGLE_CLASS: + case mxINT8_CLASS: + case mxUINT8_CLASS: + case mxINT16_CLASS: + case mxUINT16_CLASS: + case mxINT32_CLASS: + case mxUINT32_CLASS: + default: + // handle all these numerical formats as double arrays + + // real data subelement + { + std::streampos pos; + + if (read_mat5_tag (is, swap, type, len)) + { + error ("load: reading matrix data for `%s'", name); + goto data_read_error; + } + + pos = is.tellg (); + read_mat5_binary_data (is, re.fortran_vec (), nr*nc, swap, + (enum mat5_data_type) type, flt_fmt); + + if (! is || error_state) + { + error ("load: reading matrix data for `%s'", name); + goto data_read_error; + } + + is.seekg (pos + PAD (len)); + } + + // imaginary data subelement + if (imag) + { + Matrix im (nr, nc); + + if (read_mat5_tag (is, swap, type, len)) + { + error ("load: reading matrix data for `%s'", name); + goto data_read_error; + } + + read_mat5_binary_data (is, im.fortran_vec (), nr*nc, swap, + (enum mat5_data_type) type, flt_fmt); + + if (! is || error_state) + { + error ("load: reading imaginary matrix data for `%s'", name); + goto data_read_error; + } + + ComplexMatrix ctmp (nr, nc); + + for (int j = 0; j < nc; j++) + for (int i = 0; i < nr; i++) + ctmp (i, j) = Complex (re (i, j), im (i, j)); + + tc = ctmp; + } + else + tc = re; + + if (arrayclass == mxCHAR_CLASS) + tc = tc.convert_to_str (); + } + + is.seekg (pos + element_length); + + return name; + + data_read_error: + delete [] name; + + early_read_error: + error ("load: trouble reading binary file `%s'", filename.c_str ()); + return 0; + + skip_ahead: + warning (" skipping over `%s'", name); + delete [] name; + is.seekg (pos + element_length); + return read_mat5_binary_element (is, filename, swap, global, tc); +} + +static int +read_mat5_binary_file_header (std::istream& is, bool& swap, + bool quiet = false) +{ + TWO_BYTE_INT version=0, magic=0; + + is.seekg (124, std::ios::beg); + is.read (X_CAST (char *, &version), 2); + is.read (X_CAST (char *, &magic), 2); + + if (magic == 0x4d49) + swap = 0; + else if (magic == 0x494d) + swap = 1; + else + { + if (! quiet) + error ("load: can't read binary file"); + return -1; + } + + if (! swap) // version number is inverse swapped! + version = ((version >> 8) & 0xff) + ((version & 0xff) << 8); + + if (version != 1 && !quiet) + warning ("load: found version %d binary MAT file, " + "but only prepared for version 1", version); + + return 0; +} + // Return TRUE if NAME matches one of the given globbing PATTERNS. static bool @@ -2298,6 +2794,7 @@ if (pattern.match (name)) return true; } + return false; } @@ -2310,6 +2807,7 @@ char magic[magic_len+1]; is.read (X_CAST (char *, magic), magic_len); magic[magic_len] = '\0'; + if (strncmp (magic, "Octave-1-L", magic_len) == 0) swap = oct_mach_info::words_big_endian (); else if (strncmp (magic, "Octave-1-B", magic_len) == 0) @@ -2330,6 +2828,7 @@ { if (! quiet) error ("load: unrecognized binary format!"); + return -1; } @@ -2342,7 +2841,7 @@ load_save_format retval = LS_UNKNOWN; #ifdef HAVE_HDF5 - // check this before we open the file + // check this before we open the file if (H5Fis_hdf5 (fname.c_str ()) > 0) return LS_HDF5; #endif /* HAVE_HDF5 */ @@ -2376,24 +2875,38 @@ file.clear (); file.seekg (0, std::ios::beg); - char *tmp = extract_keyword (file, "name"); - - if (tmp) - { - retval = LS_ASCII; - - delete [] tmp; - } - else - { - // Try reading the file as numbers only, determining the - // number of rows and columns from the data. We don't - // even bother to check to see if the first item in the - // file is a number, so that get_complete_line () can - // skip any comments that might appear at the top of the - // file. - - retval = LS_MAT_ASCII; + err = read_mat5_binary_file_header (file, swap, false); + + if (! err) + { + file.clear (); + file.seekg (0, std::ios::beg); + retval = LS_MAT5_BINARY; + } + else + { + file.clear (); + file.seekg (0, std::ios::beg); + + char *tmp = extract_keyword (file, "name"); + + if (tmp) + { + retval = LS_ASCII; + + delete [] tmp; + } + else + { + // Try reading the file as numbers only, determining the + // number of rows and columns from the data. We don't + // even bother to check to see if the first item in the + // file is a number, so that get_complete_line() can + // skip any comments that might appear at the top of the + // file. + + retval = LS_MAT_ASCII; + } } } } @@ -2451,6 +2964,11 @@ break; #endif /* HAVE_HDF5 */ + case LS_MAT5_BINARY: + name = read_mat5_binary_element (stream, orig_fname, swap, + global, tc); + break; + default: gripe_unrecognized_data_fmt ("load"); break; @@ -2600,6 +3118,10 @@ @item -mat-binary\n\ Force Octave to assume the file is in @sc{Matlab}'s binary format.\n\ \n\ +@item -mat4-binary\n\ +Force Octave to assume the file is in the binary format written by\n\ +@sc{Matlab} version 4.\n\ +\n\ @item -hdf5\n\ Force Octave to assume the file is in HDF5 format.\n\ (HDF5 is a free, portable binary format developed by the National\n\ @@ -2667,6 +3189,10 @@ } else if (argv[i] == "-mat-binary" || argv[i] == "-m") { + format = LS_MAT5_BINARY; + } + else if (argv[i] == "-mat4-binary" || argv[i] == "-4") + { format = LS_MAT_BINARY; } else if (argv[i] == "-hdf5" || argv[i] == "-h") @@ -2756,7 +3282,9 @@ i++; unsigned mode = std::ios::in; - if (format == LS_BINARY || format == LS_MAT_BINARY) + if (format == LS_BINARY || + format == LS_MAT_BINARY || + format == LS_MAT5_BINARY) mode |= std::ios::binary; std::ifstream file (fname.c_str (), mode); @@ -2771,6 +3299,14 @@ return retval; } } + else if (format == LS_MAT5_BINARY) + { + if (read_mat5_binary_file_header (file, swap, false) < 0) + { + file.close (); + return retval; + } + } retval = do_load (file, orig_fname, force, format, flt_fmt, list_only, swap, verbose, import, @@ -3301,7 +3837,7 @@ // mark with an attribute "OCTAVE_LIST" with value 1 // to distinguish from structures (also stored as HDF5 groups): - if (hdf5_add_attr(data_id, "OCTAVE_LIST") < 0) + if (hdf5_add_attr (data_id, "OCTAVE_LIST") < 0) goto error_cleanup; } else if (tc.is_map ()) @@ -3343,7 +3879,7 @@ // if it's global, add an attribute "OCTAVE_GLOBAL" with value 1 if (mark_as_global) - retval = hdf5_add_attr(data_id, "OCTAVE_GLOBAL") >= 0; + retval = hdf5_add_attr (data_id, "OCTAVE_GLOBAL") >= 0; error_cleanup: @@ -3383,8 +3919,348 @@ #endif /* HAVE_HDF5 */ +static int +write_mat5_tag (std::ostream& is, int type, int bytes) +{ + FOUR_BYTE_INT temp; + + if (bytes <= 4) + temp = (bytes << 16) + type; + else + { + temp = type; + if (! is.write ((char *)&temp, 4)) + goto data_write_error; + temp = bytes; + } + + if (! is.write ((char *)&temp, 4)) + goto data_write_error; + + return 0; + + data_write_error: + return 1; +} + +// write out the numeric values in M to OS, +// preceded by the appropriate tag. +static void +write_mat5_array (std::ostream& os, Matrix& m, const int save_as_floats) +{ + int nr = m.rows (); + int nc = m.columns (); + double max_val, min_val; + save_type st = LS_DOUBLE; + mat5_data_type mst; + int size; + unsigned len; + double *data = m.fortran_vec (); + +// Have to use copy here to avoid writing over data accessed via +// Matrix::data(). + +#define MAT5_DO_WRITE(TYPE, data, count, stream) \ + do \ + { \ + TYPE *ptr = new TYPE [count]; \ + for (int i = 0; i < count; i++) \ + ptr[i] = X_CAST (TYPE, data[i]); \ + stream.write (X_CAST (char *, ptr), count * sizeof (TYPE)); \ + delete [] ptr ; \ + } \ + while (0) + + if (save_as_floats) + { + if (m.too_large_for_float ()) + { + warning ("save: some values too large to save as floats --"); + warning ("save: saving as doubles instead"); + } + else + st = LS_FLOAT; + } + + if (m.all_integers (max_val, min_val)) + st = get_save_type (max_val, min_val); + + switch (st) + { + default: + case LS_DOUBLE: mst = miDOUBLE; size = 8; break; + case LS_FLOAT: mst = miSINGLE; size = 4; break; + case LS_U_CHAR: mst = miUINT8; size = 1; break; + case LS_U_SHORT: mst = miUINT16; size = 2; break; + case LS_U_INT: mst = miUINT32; size = 4; break; + case LS_CHAR: mst = miINT8; size = 1; break; + case LS_SHORT: mst = miINT16; size = 2; break; + case LS_INT: mst = miINT32; size = 4; break; + } + + len = nr*nc*size; + write_mat5_tag (os, mst, len); + + { + switch (st) + { + case LS_U_CHAR: + MAT5_DO_WRITE (unsigned char, data, nr*nc, os); + break; + + case LS_U_SHORT: + MAT5_DO_WRITE (unsigned TWO_BYTE_INT, data, nr*nc, os); + break; + + case LS_U_INT: + MAT5_DO_WRITE (unsigned FOUR_BYTE_INT, data, nr*nc, os); + break; + + // provide for 64 bit ints, even though get_save_type does + // not yet implement them +#ifdef EIGHT_BYTE_INT + case LS_U_LONG: + MAT5_DO_WRITE (unsigned EIGHT_BYTE_INT, data, nr*nc, os); + break; +#endif + + case LS_CHAR: + MAT5_DO_WRITE (signed char, data, nr*nc, os); + break; + + case LS_SHORT: + MAT5_DO_WRITE (TWO_BYTE_INT, data, nr*nc, os); + break; + + case LS_INT: + MAT5_DO_WRITE (FOUR_BYTE_INT, data, nr*nc, os); + break; + +#ifdef EIGHT_BYTE_INT + case LS_LONG: + MAT5_DO_WRITE (EIGHT_BYTE_INT, data, nr*nc, os); + break; +#endif + + case LS_FLOAT: + MAT5_DO_WRITE (float, data, nr*nc, os); + break; + + case LS_DOUBLE: // No conversion necessary. + os.write (X_CAST (char *, data), len); + break; + + default: + (*current_liboctave_error_handler) + ("unrecognized data format requested"); + break; + } + } + if (PAD (len) > len) + { + static char buf[9]="\x00\x00\x00\x00\x00\x00\x00\x00"; + os.write (buf, PAD (len) - len); + } +} + +// save the data from TC along with the corresponding NAME on stream +// OS in the MatLab version 5 binary format. Return true on success. + +static bool +save_mat5_binary_element (std::ostream& os, + const octave_value& tc, const std::string& name, + bool mark_as_global, bool save_as_floats) +{ + FOUR_BYTE_INT flags=0; + FOUR_BYTE_INT junk=0; + FOUR_BYTE_INT nr; + FOUR_BYTE_INT nc; + streampos fixup, contin; + + // element type and length + fixup = os.tellp (); + write_mat5_tag (os, miMATRIX, 99); // we don't know the real length yet + + // array flags subelement + write_mat5_tag (os, miUINT32, 8); + + if (mark_as_global) + flags |= 0x0400; + + if (tc.is_complex_scalar () || tc.is_complex_matrix ()) + flags |= 0x0800; + + if (tc.is_string ()) + flags |= mxCHAR_CLASS; + else if (tc.is_real_scalar ()) + flags |= mxDOUBLE_CLASS; + else if (tc.is_real_matrix ()) + flags |= mxDOUBLE_CLASS; + else if (tc.is_complex_scalar ()) + flags |= mxDOUBLE_CLASS; + else if (tc.is_complex_matrix ()) + flags |= mxDOUBLE_CLASS; + else if (tc.is_map ()) + flags |= mxSTRUCT_CLASS; + else + { + gripe_wrong_type_arg ("save", tc, false); + goto error_cleanup; + } + + os.write ((char *)&flags, 4); + os.write ((char *)&junk, 4); + + // dimensions array subelement + { + if (tc.is_string ()) + { + charMatrix chm = tc.char_matrix_value (); + nr = tc.rows (); + nc = chm.cols (); + } + else if (tc.is_real_scalar () || tc.is_complex_scalar () || tc.is_map ()) + { + nr = nc = 1; + } + else if (tc.is_real_matrix ()) + { + Matrix m = tc.matrix_value (); + nr = m.rows (); + nc = m.columns (); + } + else if (tc.is_complex_matrix ()) + { + ComplexMatrix m = tc.complex_matrix_value (); + nr = m.rows (); + nc = m.columns (); + } + + write_mat5_tag (os, miINT32, 8); + os.write ((char *)&nr, 4); + os.write ((char *)&nc, 4); + } + + // array name subelement + { + int namelen = name.length (); + + if (namelen > 31) + namelen = 31; // only 31 char names permitted in mat file + + int paddedlength = PAD (namelen); + + write_mat5_tag (os, miINT8, namelen); + char * paddedname = new char [paddedlength]; + memset (paddedname, 0, paddedlength); + strncpy (paddedname, name.c_str (), namelen); + os.write (paddedname, paddedlength); + delete [] paddedname; + } + + // data element + if (tc.is_string ()) + { + charMatrix chm = tc.char_matrix_value (); + int nc = chm.cols (); + int len = nr*nc*2; + int paddedlength = PAD (nr*nc*2); + + TWO_BYTE_INT *buf = new TWO_BYTE_INT[nc+3]; + write_mat5_tag (os, miUINT16, len); + + for (int i = 0; i < nr; i++) + { + std::string tstr = chm.row_as_string (i); + const char *s = tstr.data (); + + for (int j = 0; j < nc; j++) + buf[j] = *s++; + + os.write ((char *)buf, nc*2); + } + + if (paddedlength > len) + os.write ((char *)buf, paddedlength - len); + + delete [] buf; + } + else if (tc.is_real_scalar () || tc.is_real_matrix ()) + { + Matrix m = tc.matrix_value (); + + write_mat5_array (os, m, save_as_floats); + } + else if (tc.is_complex_scalar () || tc.is_complex_matrix ()) + { + ComplexMatrix m_cmplx = tc.complex_matrix_value (); + Matrix m = ::real (m_cmplx); + + for (int part=0; part < 2; part++) + { + // real part, then complex part + write_mat5_array (os, m, save_as_floats); + m = ::imag (m_cmplx); + } + } + else if (tc.is_map ()) + { + // an Octave structure */ + // recursively write each element of the structure + Octave_map m = tc.map_value (); + Pix i; + + { + char buf[32]; + FOUR_BYTE_INT maxfieldnamelength = 32; + int fieldcnt = 0; + + for (i = m.first (); i; m.next (i)) + fieldcnt++; + + write_mat5_tag (os, miINT32, 4); + os.write ((char *)&maxfieldnamelength, 4); + write_mat5_tag (os, miINT8, fieldcnt*32); + + for (i = m.first (); i; m.next (i)) + { + // write the name of each element + string tstr = m.key (i); + memset (buf, 0, 32); + strncpy (buf, tstr.c_str (), 31); // only 31 char names permitted + os.write (buf, 32); + } + + for (i = m.first (); i; m.next (i)) + { + // write the data of each element + bool retval2 = save_mat5_binary_element (os, m.contents (i), "", + mark_as_global, + save_as_floats); + + if (! retval2) + goto error_cleanup; + } + } + } + else + gripe_wrong_type_arg ("save", tc, false); + + contin = os.tellp (); + os.seekp (fixup); + write_mat5_tag (os, miMATRIX, contin - fixup - 8); // the actual length + os.seekp (contin); + + return true; + + error_cleanup: + error ("save: error while writing `%s' to MAT file", name.c_str ()); + + return false; +} + // Save the data from TC along with the corresponding NAME on stream OS -// in the MatLab binary format. +// in the MatLab version 4 binary format. static bool save_mat_binary_data (std::ostream& os, const octave_value& tc, @@ -3459,9 +4335,9 @@ else if (tc.is_complex_matrix ()) { ComplexMatrix m_cmplx = tc.complex_matrix_value (); - Matrix m = ::real(m_cmplx); + Matrix m = ::real (m_cmplx); os.write (X_CAST (char *, m.data ()), 8 * len); - m = ::imag(m_cmplx); + m = ::imag (m_cmplx); os.write (X_CAST (char *, m.data ()), 8 * len); } else @@ -3729,6 +4605,10 @@ break; #endif /* HAVE_HDF5 */ + case LS_MAT5_BINARY: + save_mat5_binary_element (os, tc, name, global, save_as_floats); + break; + default: gripe_unrecognized_data_fmt ("save"); break; @@ -3787,6 +4667,8 @@ if (fmt == "binary") retval = LS_BINARY; else if (fmt == "mat-binary" || fmt =="mat_binary") + retval = LS_MAT5_BINARY; + else if (fmt == "mat4-binary" || fmt =="mat4_binary") retval = LS_MAT_BINARY; #ifdef HAVE_HDF5 else if (fmt == "hdf5") @@ -3813,7 +4695,40 @@ os.write (X_CAST (char *, &tmp), 1); } - break; + break; + + case LS_MAT5_BINARY: + { + char *versionmagic; + TWO_BYTE_INT number = *(TWO_BYTE_INT *)"\x00\x01"; + struct tm bdt; + time_t now; + char headertext[128]; + + time (&now); + bdt = *gmtime (&now); + memset (headertext, ' ', 124); + // ISO 8601 format date + strftime (headertext, 124, "MATLAB 5.0 MAT-file, written by Octave " + OCTAVE_VERSION ", %Y-%m-%d %T UTC", &bdt); + + // The first pair of bytes give the version of the MAT file + // format. The second pair of bytes form a magic number which + // signals a MAT file. MAT file data are always written in + // native byte order. The order of the bytes in the second + // pair indicates whether the file was written by a big- or + // little-endian machine. However, the version number is + // written in the *opposite* byte order from everything else! + if (number == 1) + versionmagic = "\x01\x00\x4d\x49"; // this machine is big endian + else + versionmagic = "\x00\x01\x49\x4d"; // this machine is little endian + + memcpy (headertext+124, versionmagic, 4); + os.write (headertext, 128); + } + + break; #ifdef HAVE_HDF5 case LS_HDF5: @@ -3898,7 +4813,9 @@ load_save_format format = get_default_save_format (); unsigned mode = std::ios::out|std::ios::trunc; - if (format == LS_BINARY || format == LS_MAT_BINARY) + if (format == LS_BINARY || + format == LS_MAT_BINARY || + format == LS_MAT5_BINARY) mode |= std::ios::binary; #ifdef HAVE_HDF5 @@ -3964,6 +4881,9 @@ @item -mat-binary\n\ Save the data in @sc{Matlab}'s binary data format.\n\ \n\ +@item -mat4-binary\n\ +Save the data in the binary format written by @sc{Matlab} version 4.\n\ +\n\ @item -hdf5\n\ Save the data in HDF5 format.\n\ (HDF5 is a free, portable binary format developed by the National\n\ @@ -4060,6 +4980,10 @@ } else if (argv[i] == "-mat-binary" || argv[i] == "-m") { + format = LS_MAT5_BINARY; + } + else if (argv[i] == "-mat4-binary" || argv[i] == "-4") + { format = LS_MAT_BINARY; } else if (argv[i] == "-float-binary" || argv[i] == "-f") @@ -4131,7 +5055,9 @@ i++; unsigned mode = std::ios::out; - if (format == LS_BINARY || format == LS_MAT_BINARY) + if (format == LS_BINARY || + format == LS_MAT_BINARY || + format == LS_MAT5_BINARY) mode |= std::ios::binary; mode |= append ? std::ios::ate : std::ios::trunc;