ls-mat5.cc

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 1996-2012 John W. Eaton
00004 
00005 This file is part of Octave.
00006 
00007 Octave is free software; you can redistribute it and/or modify it
00008 under the terms of the GNU General Public License as published by the
00009 Free Software Foundation; either version 3 of the License, or (at your
00010 option) any later version.
00011 
00012 Octave is distributed in the hope that it will be useful, but WITHOUT
00013 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00015 for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Octave; see the file COPYING.  If not, see
00019 <http://www.gnu.org/licenses/>.
00020 
00021 */
00022 
00023 // Author: James R. Van Zandt <jrv@vanzandt.mv.com>
00024 
00025 #ifdef HAVE_CONFIG_H
00026 #include <config.h>
00027 #endif
00028 
00029 #include <cfloat>
00030 #include <cstring>
00031 #include <cctype>
00032 
00033 #include <fstream>
00034 #include <iomanip>
00035 #include <iostream>
00036 #include <sstream>
00037 #include <string>
00038 #include <vector>
00039 
00040 #include "byte-swap.h"
00041 #include "data-conv.h"
00042 #include "file-ops.h"
00043 #include "glob-match.h"
00044 #include "lo-mappers.h"
00045 #include "mach-info.h"
00046 #include "oct-env.h"
00047 #include "oct-time.h"
00048 #include "quit.h"
00049 #include "str-vec.h"
00050 #include "file-stat.h"
00051 #include "oct-locbuf.h"
00052 
00053 #include "Cell.h"
00054 #include "defun.h"
00055 #include "error.h"
00056 #include "gripes.h"
00057 #include "load-save.h"
00058 #include "load-path.h"
00059 #include "oct-obj.h"
00060 #include "oct-map.h"
00061 #include "ov-cell.h"
00062 #include "ov-class.h"
00063 #include "ov-fcn-inline.h"
00064 #include "pager.h"
00065 #include "pt-exp.h"
00066 #include "sysdep.h"
00067 #include "toplev.h"
00068 #include "unwind-prot.h"
00069 #include "utils.h"
00070 #include "variables.h"
00071 #include "version.h"
00072 #include "dMatrix.h"
00073 
00074 #include "ls-utils.h"
00075 #include "ls-mat5.h"
00076 
00077 #include "parse.h"
00078 #include "defaults.h"
00079 
00080 #ifdef HAVE_ZLIB
00081 #include <zlib.h>
00082 #endif
00083 
00084 #define PAD(l) (((l) > 0 && (l) <= 4) ? 4 : (((l)+7)/8)*8)
00085 
00086 
00087 // The subsystem data block
00088 static octave_value subsys_ov;
00089 
00090 // FIXME -- the following enum values should be the same as the
00091 // mxClassID values in mexproto.h, but it seems they have also changed
00092 // over time.  What is the correct way to handle this and maintain
00093 // backward compatibility with old MAT files?  For now, use
00094 // "MAT_FILE_" instead of "mx" as the prefix for these names to avoid
00095 // conflict with the mxClassID enum in mexproto.h.
00096 
00097 enum arrayclasstype
00098   {
00099     MAT_FILE_CELL_CLASS=1,              // cell array
00100     MAT_FILE_STRUCT_CLASS,              // structure
00101     MAT_FILE_OBJECT_CLASS,              // object
00102     MAT_FILE_CHAR_CLASS,                // character array
00103     MAT_FILE_SPARSE_CLASS,              // sparse array
00104     MAT_FILE_DOUBLE_CLASS,              // double precision array
00105     MAT_FILE_SINGLE_CLASS,              // single precision floating point
00106     MAT_FILE_INT8_CLASS,                // 8 bit signed integer
00107     MAT_FILE_UINT8_CLASS,               // 8 bit unsigned integer
00108     MAT_FILE_INT16_CLASS,               // 16 bit signed integer
00109     MAT_FILE_UINT16_CLASS,              // 16 bit unsigned integer
00110     MAT_FILE_INT32_CLASS,               // 32 bit signed integer
00111     MAT_FILE_UINT32_CLASS,              // 32 bit unsigned integer
00112     MAT_FILE_INT64_CLASS,               // 64 bit signed integer
00113     MAT_FILE_UINT64_CLASS,              // 64 bit unsigned integer
00114     MAT_FILE_FUNCTION_CLASS,            // Function handle
00115     MAT_FILE_WORKSPACE_CLASS            // Workspace (undocumented)
00116   };
00117 
00118 // Read COUNT elements of data from IS in the format specified by TYPE,
00119 // placing the result in DATA.  If SWAP is TRUE, swap the bytes of
00120 // each element before copying to DATA.  FLT_FMT specifies the format
00121 // of the data if we are reading floating point numbers.
00122 
00123 static void
00124 read_mat5_binary_data (std::istream& is, double *data,
00125                        octave_idx_type  count, bool swap, mat5_data_type type,
00126                        oct_mach_info::float_format flt_fmt)
00127 {
00128 
00129   switch (type)
00130     {
00131     case miINT8:
00132       read_doubles (is, data, LS_CHAR, count, swap, flt_fmt);
00133       break;
00134 
00135     case miUTF8:
00136     case miUINT8:
00137       read_doubles (is, data, LS_U_CHAR, count, swap, flt_fmt);
00138       break;
00139 
00140     case miINT16:
00141       read_doubles (is, data, LS_SHORT, count, swap, flt_fmt);
00142       break;
00143 
00144     case miUTF16:
00145     case miUINT16:
00146       read_doubles (is, data, LS_U_SHORT, count, swap, flt_fmt);
00147       break;
00148 
00149     case miINT32:
00150       read_doubles (is, data, LS_INT, count, swap, flt_fmt);
00151       break;
00152 
00153     case miUTF32:
00154     case miUINT32:
00155       read_doubles (is, data, LS_U_INT, count, swap, flt_fmt);
00156       break;
00157 
00158     case miSINGLE:
00159       read_doubles (is, data, LS_FLOAT, count, swap, flt_fmt);
00160       break;
00161 
00162     case miRESERVE1:
00163       break;
00164 
00165     case miDOUBLE:
00166       read_doubles (is, data, LS_DOUBLE, count, swap, flt_fmt);
00167       break;
00168 
00169     case miRESERVE2:
00170     case miRESERVE3:
00171       break;
00172 
00173     // FIXME -- how are the 64-bit cases supposed to work here?
00174     case miINT64:
00175       read_doubles (is, data, LS_LONG, count, swap, flt_fmt);
00176       break;
00177 
00178     case miUINT64:
00179       read_doubles (is, data, LS_U_LONG, count, swap, flt_fmt);
00180       break;
00181 
00182     case miMATRIX:
00183     default:
00184       break;
00185     }
00186 }
00187 
00188 static void
00189 read_mat5_binary_data (std::istream& is, float *data,
00190                        octave_idx_type  count, bool swap, mat5_data_type type,
00191                        oct_mach_info::float_format flt_fmt)
00192 {
00193 
00194   switch (type)
00195     {
00196     case miINT8:
00197       read_floats (is, data, LS_CHAR, count, swap, flt_fmt);
00198       break;
00199 
00200     case miUTF8:
00201     case miUINT8:
00202       read_floats (is, data, LS_U_CHAR, count, swap, flt_fmt);
00203       break;
00204 
00205     case miINT16:
00206       read_floats (is, data, LS_SHORT, count, swap, flt_fmt);
00207       break;
00208 
00209     case miUTF16:
00210     case miUINT16:
00211       read_floats (is, data, LS_U_SHORT, count, swap, flt_fmt);
00212       break;
00213 
00214     case miINT32:
00215       read_floats (is, data, LS_INT, count, swap, flt_fmt);
00216       break;
00217 
00218     case miUTF32:
00219     case miUINT32:
00220       read_floats (is, data, LS_U_INT, count, swap, flt_fmt);
00221       break;
00222 
00223     case miSINGLE:
00224       read_floats (is, data, LS_FLOAT, count, swap, flt_fmt);
00225       break;
00226 
00227     case miRESERVE1:
00228       break;
00229 
00230     case miDOUBLE:
00231       read_floats (is, data, LS_DOUBLE, count, swap, flt_fmt);
00232       break;
00233 
00234     case miRESERVE2:
00235     case miRESERVE3:
00236       break;
00237 
00238     // FIXME -- how are the 64-bit cases supposed to work here?
00239     case miINT64:
00240       read_floats (is, data, LS_LONG, count, swap, flt_fmt);
00241       break;
00242 
00243     case miUINT64:
00244       read_floats (is, data, LS_U_LONG, count, swap, flt_fmt);
00245       break;
00246 
00247     case miMATRIX:
00248     default:
00249       break;
00250     }
00251 }
00252 
00253 template <class T>
00254 void
00255 read_mat5_integer_data (std::istream& is, T *m, octave_idx_type count,
00256                         bool swap, mat5_data_type type)
00257 {
00258 
00259 #define READ_INTEGER_DATA(TYPE, swap, data, size, len, stream)  \
00260   do \
00261     { \
00262       if (len > 0) \
00263         { \
00264           OCTAVE_LOCAL_BUFFER (TYPE, ptr, len); \
00265           stream.read (reinterpret_cast<char *> (ptr), size * len); \
00266           if (swap) \
00267             swap_bytes< size > (ptr, len); \
00268           for (octave_idx_type i = 0; i < len; i++) \
00269             data[i] = ptr[i]; \
00270         } \
00271     } \
00272   while (0)
00273 
00274   switch (type)
00275     {
00276     case miINT8:
00277       READ_INTEGER_DATA (int8_t, swap, m, 1, count, is);
00278       break;
00279 
00280     case miUINT8:
00281       READ_INTEGER_DATA (uint8_t, swap, m, 1, count, is);
00282       break;
00283 
00284     case miINT16:
00285       READ_INTEGER_DATA (int16_t, swap, m, 2, count, is);
00286       break;
00287 
00288     case miUINT16:
00289       READ_INTEGER_DATA (uint16_t, swap, m, 2, count, is);
00290       break;
00291 
00292     case miINT32:
00293       READ_INTEGER_DATA (int32_t, swap, m, 4, count, is);
00294       break;
00295 
00296     case miUINT32:
00297       READ_INTEGER_DATA (uint32_t, swap, m, 4, count, is);
00298       break;
00299 
00300     case miSINGLE:
00301     case miRESERVE1:
00302     case miDOUBLE:
00303     case miRESERVE2:
00304     case miRESERVE3:
00305       break;
00306 
00307     case miINT64:
00308       READ_INTEGER_DATA (int64_t, swap, m, 8, count, is);
00309       break;
00310 
00311     case miUINT64:
00312       READ_INTEGER_DATA (uint64_t, swap, m, 8, count, is);
00313       break;
00314 
00315     case miMATRIX:
00316     default:
00317       break;
00318     }
00319 
00320 #undef READ_INTEGER_DATA
00321 
00322 }
00323 
00324 template void
00325 read_mat5_integer_data (std::istream& is, octave_int8 *m,
00326                         octave_idx_type count, bool swap,
00327                         mat5_data_type type);
00328 
00329 template void
00330 read_mat5_integer_data (std::istream& is, octave_int16 *m,
00331                         octave_idx_type count, bool swap,
00332                         mat5_data_type type);
00333 
00334 template void
00335 read_mat5_integer_data (std::istream& is, octave_int32 *m,
00336                         octave_idx_type count, bool swap,
00337                         mat5_data_type type);
00338 
00339 template void
00340 read_mat5_integer_data (std::istream& is, octave_int64 *m,
00341                         octave_idx_type count, bool swap,
00342                         mat5_data_type type);
00343 
00344 template void
00345 read_mat5_integer_data (std::istream& is, octave_uint8 *m,
00346                         octave_idx_type count, bool swap,
00347                         mat5_data_type type);
00348 
00349 template void
00350 read_mat5_integer_data (std::istream& is, octave_uint16 *m,
00351                         octave_idx_type count, bool swap,
00352                         mat5_data_type type);
00353 
00354 template void
00355 read_mat5_integer_data (std::istream& is, octave_uint32 *m,
00356                         octave_idx_type count, bool swap,
00357                         mat5_data_type type);
00358 
00359 template void
00360 read_mat5_integer_data (std::istream& is, octave_uint64 *m,
00361                         octave_idx_type count, bool swap,
00362                         mat5_data_type type);
00363 
00364 template void
00365 read_mat5_integer_data (std::istream& is, int *m,
00366                         octave_idx_type count, bool swap,
00367                         mat5_data_type type);
00368 
00369 #define OCTAVE_MAT5_INTEGER_READ(TYP) \
00370   { \
00371         TYP re (dims); \
00372   \
00373         std::streampos tmp_pos; \
00374   \
00375         if (read_mat5_tag (is, swap, type, len)) \
00376           { \
00377             error ("load: reading matrix data for '%s'", retval.c_str ()); \
00378             goto data_read_error; \
00379           } \
00380   \
00381         octave_idx_type n = re.numel (); \
00382         tmp_pos = is.tellg (); \
00383         read_mat5_integer_data (is, re.fortran_vec (), n, swap, \
00384                                 static_cast<enum mat5_data_type> (type)); \
00385   \
00386         if (! is || error_state) \
00387           { \
00388             error ("load: reading matrix data for '%s'", retval.c_str ()); \
00389             goto data_read_error; \
00390           } \
00391   \
00392         is.seekg (tmp_pos + static_cast<std::streamoff> (PAD (len))); \
00393   \
00394         if (imag) \
00395           { \
00396             /* We don't handle imag integer types, convert to an array */ \
00397             NDArray im (dims); \
00398   \
00399             if (read_mat5_tag (is, swap, type, len)) \
00400               { \
00401                 error ("load: reading matrix data for '%s'", \
00402                        retval.c_str ()); \
00403                 goto data_read_error; \
00404               } \
00405   \
00406             n = im.numel (); \
00407             read_mat5_binary_data (is, im.fortran_vec (), n, swap, \
00408                                    static_cast<enum mat5_data_type> (type), flt_fmt); \
00409   \
00410             if (! is || error_state) \
00411               { \
00412                 error ("load: reading imaginary matrix data for '%s'", \
00413                        retval.c_str ()); \
00414                 goto data_read_error; \
00415               } \
00416   \
00417             ComplexNDArray ctmp (dims); \
00418   \
00419             for (octave_idx_type i = 0; i < n; i++) \
00420               ctmp(i) = Complex (re(i).double_value (), im(i)); \
00421   \
00422             tc = ctmp;  \
00423           } \
00424         else \
00425           tc = re; \
00426   }
00427 
00428 // Read one element tag from stream IS,
00429 // place the type code in TYPE and the byte count in BYTES
00430 // return nonzero on error
00431 static int
00432 read_mat5_tag (std::istream& is, bool swap, int32_t& type, int32_t& bytes)
00433 {
00434   unsigned int upper;
00435   int32_t temp;
00436 
00437   if (! is.read (reinterpret_cast<char *> (&temp), 4 ))
00438     goto data_read_error;
00439 
00440   if (swap)
00441     swap_bytes<4> (&temp);
00442 
00443   upper = (temp >> 16) & 0xffff;
00444   type = temp & 0xffff;
00445 
00446   if (upper)
00447     {
00448       // "compressed" format
00449       bytes = upper;
00450     }
00451   else
00452     {
00453       if (! is.read (reinterpret_cast<char *> (&temp), 4 ))
00454         goto data_read_error;
00455       if (swap)
00456         swap_bytes<4> (&temp);
00457       bytes = temp;
00458     }
00459 
00460   return 0;
00461 
00462  data_read_error:
00463   return 1;
00464 }
00465 
00466 static void
00467 read_int (std::istream& is, bool swap, int32_t& val)
00468 {
00469   is.read (reinterpret_cast<char *> (&val), 4);
00470 
00471   if (swap)
00472     swap_bytes<4> (&val);
00473 }
00474 
00475 // Extract one data element (scalar, matrix, string, etc.) from stream
00476 // IS and place it in TC, returning the name of the variable.
00477 //
00478 // The data is expected to be in Matlab's "Version 5" .mat format,
00479 // though not all the features of that format are supported.
00480 //
00481 // FILENAME is used for error messages.
00482 
00483 std::string
00484 read_mat5_binary_element (std::istream& is, const std::string& filename,
00485                           bool swap, bool& global, octave_value& tc)
00486 {
00487   std::string retval;
00488 
00489   global = false;
00490 
00491   // NOTE: these are initialized here instead of closer to where they
00492   // are first used to avoid errors from gcc about goto crossing
00493   // initialization of variable.
00494 
00495   bool imag;
00496   bool isclass = false;
00497   bool logicalvar;
00498   dim_vector dims;
00499   enum arrayclasstype arrayclass;
00500   int16_t number = *(reinterpret_cast<const int16_t *>("\x00\x01"));
00501   octave_idx_type nzmax;
00502   std::string classname;
00503 
00504   // MAT files always use IEEE floating point
00505   oct_mach_info::float_format flt_fmt = oct_mach_info::flt_fmt_unknown;
00506   if ((number == 1) ^ swap)
00507     flt_fmt = oct_mach_info::flt_fmt_ieee_big_endian;
00508   else
00509     flt_fmt = oct_mach_info::flt_fmt_ieee_little_endian;
00510 
00511   // element type and length
00512   int32_t type = 0;
00513   int32_t element_length;
00514   if (read_mat5_tag (is, swap, type, element_length))
00515     return retval;                      // EOF
00516 
00517 #ifdef HAVE_ZLIB
00518   if (type == miCOMPRESSED)
00519     {
00520       // If C++ allowed us direct access to the file descriptor of an
00521       // ifstream in a uniform way, the code below could be vastly
00522       // simplified, and additional copies of the data in memory
00523       // wouldn't be needed.
00524 
00525       OCTAVE_LOCAL_BUFFER (char, inbuf, element_length);
00526       is.read (inbuf, element_length);
00527 
00528       // We uncompress the first 8 bytes of the header to get the buffer length
00529       // This will fail with an error Z_MEM_ERROR
00530       uLongf destLen = 8;
00531       OCTAVE_LOCAL_BUFFER (unsigned int, tmp, 2);
00532       if (uncompress (reinterpret_cast<Bytef *> (tmp), &destLen,
00533                       reinterpret_cast<Bytef *> (inbuf), element_length)
00534           !=  Z_MEM_ERROR)
00535         {
00536           // Why should I have to initialize outbuf as I'll just overwrite!!
00537           if (swap)
00538             swap_bytes<4> (tmp, 2);
00539 
00540           destLen = tmp[1] + 8;
00541           std::string outbuf (destLen, ' ');
00542 
00543           // FIXME -- find a way to avoid casting away const here!
00544 
00545           int err = uncompress (reinterpret_cast<Bytef *> (const_cast<char *> (outbuf.c_str ())),
00546                                 &destLen, reinterpret_cast<Bytef *> (inbuf),
00547                                 element_length);
00548 
00549           if (err != Z_OK)
00550             {
00551               std::string msg;
00552               switch (err)
00553                 {
00554                 case Z_STREAM_END:
00555                   msg = "stream end";
00556                   break;
00557 
00558                 case Z_NEED_DICT:
00559                   msg = "need dict";
00560                   break;
00561 
00562                 case Z_ERRNO:
00563                   msg = "errno case";
00564                   break;
00565 
00566                 case Z_STREAM_ERROR:
00567                   msg = "stream error";
00568                   break;
00569 
00570                 case Z_DATA_ERROR:
00571                   msg = "data error";
00572                   break;
00573 
00574                 case Z_MEM_ERROR:
00575                   msg = "mem error";
00576                   break;
00577 
00578                 case Z_BUF_ERROR:
00579                   msg = "buf error";
00580                   break;
00581 
00582                 case Z_VERSION_ERROR:
00583                   msg = "version error";
00584                   break;
00585                 }
00586 
00587               error ("load: error uncompressing data element (%s from zlib)",
00588                      msg.c_str ());
00589             }
00590           else
00591             {
00592               std::istringstream gz_is (outbuf);
00593               retval = read_mat5_binary_element (gz_is, filename,
00594                                                  swap, global, tc);
00595             }
00596         }
00597       else
00598         error ("load: error probing size of compressed data element");
00599 
00600       return retval;
00601     }
00602 #endif
00603 
00604   std::streampos pos;
00605 
00606   if (type != miMATRIX)
00607     {
00608       pos = is.tellg ();
00609       error ("load: invalid element type = %d", type);
00610       goto early_read_error;
00611     }
00612 
00613   if (element_length == 0)
00614     {
00615       tc = Matrix ();
00616       return retval;
00617     }
00618 
00619   pos = is.tellg ();
00620 
00621   // array flags subelement
00622   int32_t len;
00623   if (read_mat5_tag (is, swap, type, len) || type != miUINT32 || len != 8)
00624     {
00625       error ("load: invalid array flags subelement");
00626       goto early_read_error;
00627     }
00628 
00629   int32_t flags;
00630   read_int (is, swap, flags);
00631 
00632   imag = (flags & 0x0800) != 0; // has an imaginary part?
00633 
00634   global = (flags & 0x0400) != 0; // global variable?
00635 
00636   logicalvar = (flags & 0x0200) != 0; // boolean ?
00637 
00638   arrayclass = static_cast<arrayclasstype> (flags & 0xff);
00639 
00640   int32_t tmp_nzmax;
00641   read_int (is, swap, tmp_nzmax);   // max number of non-zero in sparse
00642   nzmax = tmp_nzmax;
00643 
00644   // dimensions array subelement
00645   if (arrayclass != MAT_FILE_WORKSPACE_CLASS)
00646     {
00647       int32_t dim_len;
00648 
00649       if (read_mat5_tag (is, swap, type, dim_len) || type != miINT32)
00650         {
00651           error ("load: invalid dimensions array subelement");
00652           goto early_read_error;
00653         }
00654 
00655       int ndims = dim_len / 4;
00656       dims.resize (ndims);
00657       for (int i = 0; i < ndims; i++)
00658         {
00659           int32_t n;
00660           read_int (is, swap, n);
00661           dims(i) = n;
00662         }
00663 
00664       std::streampos tmp_pos = is.tellg ();
00665       is.seekg (tmp_pos + static_cast<std::streamoff> (PAD (dim_len) - dim_len));
00666     }
00667   else
00668     {
00669       // Why did mathworks decide to not have dims for a workspace!!!
00670       dims.resize(2);
00671       dims(0) = 1;
00672       dims(1) = 1;
00673     }
00674 
00675   if (read_mat5_tag (is, swap, type, len) || type != miINT8)
00676     {
00677       error ("load: invalid array name subelement");
00678       goto early_read_error;
00679     }
00680 
00681   {
00682     OCTAVE_LOCAL_BUFFER (char, name, len+1);
00683 
00684     // Structure field subelements have zero-length array name subelements.
00685 
00686     std::streampos tmp_pos = is.tellg ();
00687 
00688     if (len)
00689       {
00690         if (! is.read (name, len ))
00691           goto data_read_error;
00692 
00693         is.seekg (tmp_pos + static_cast<std::streamoff> (PAD (len)));
00694       }
00695 
00696     name[len] = '\0';
00697     retval = name;
00698   }
00699 
00700   switch (arrayclass)
00701     {
00702     case MAT_FILE_CELL_CLASS:
00703       {
00704         Cell cell_array (dims);
00705 
00706         octave_idx_type n = cell_array.numel ();
00707 
00708         for (octave_idx_type i = 0; i < n; i++)
00709           {
00710             octave_value tc2;
00711 
00712             std::string nm
00713               = read_mat5_binary_element (is, filename, swap, global, tc2);
00714 
00715             if (! is || error_state)
00716               {
00717                 error ("load: reading cell data for '%s'", nm.c_str ());
00718                 goto data_read_error;
00719               }
00720 
00721             cell_array(i) = tc2;
00722           }
00723 
00724         tc = cell_array;
00725       }
00726       break;
00727 
00728     case MAT_FILE_SPARSE_CLASS:
00729       {
00730         octave_idx_type nr = dims(0);
00731         octave_idx_type nc = dims(1);
00732         SparseMatrix sm;
00733         SparseComplexMatrix scm;
00734         octave_idx_type *ridx;
00735         octave_idx_type *cidx;
00736         double *data;
00737 
00738         // Setup return value
00739         if (imag)
00740           {
00741             scm = SparseComplexMatrix (nr, nc, nzmax);
00742             ridx = scm.ridx ();
00743             cidx = scm.cidx ();
00744             data = 0;
00745           }
00746         else
00747           {
00748             sm = SparseMatrix (nr, nc, nzmax);
00749             ridx = sm.ridx ();
00750             cidx = sm.cidx ();
00751             data = sm.data ();
00752           }
00753 
00754         // row indices
00755         std::streampos tmp_pos;
00756 
00757         if (read_mat5_tag (is, swap, type, len))
00758           {
00759             error ("load: reading sparse row data for '%s'", retval.c_str ());
00760             goto data_read_error;
00761           }
00762 
00763         tmp_pos = is.tellg ();
00764 
00765         read_mat5_integer_data (is, ridx, nzmax, swap,
00766                                 static_cast<enum mat5_data_type> (type));
00767 
00768         if (! is || error_state)
00769           {
00770             error ("load: reading sparse row data for '%s'", retval.c_str ());
00771             goto data_read_error;
00772           }
00773 
00774         is.seekg (tmp_pos + static_cast<std::streamoff> (PAD (len)));
00775 
00776         // col indices
00777         if (read_mat5_tag (is, swap, type, len))
00778           {
00779             error ("load: reading sparse column data for '%s'", retval.c_str ());
00780             goto data_read_error;
00781           }
00782 
00783         tmp_pos = is.tellg ();
00784 
00785         read_mat5_integer_data (is, cidx, nc + 1, swap,
00786                                 static_cast<enum mat5_data_type> (type));
00787 
00788         if (! is || error_state)
00789           {
00790             error ("load: reading sparse column data for '%s'", retval.c_str ());
00791             goto data_read_error;
00792           }
00793 
00794         is.seekg (tmp_pos + static_cast<std::streamoff> (PAD (len)));
00795 
00796         // real data subelement
00797         if (read_mat5_tag (is, swap, type, len))
00798           {
00799             error ("load: reading sparse matrix data for '%s'", retval.c_str ());
00800             goto data_read_error;
00801           }
00802 
00803         octave_idx_type nnz = cidx[nc];
00804         NDArray re;
00805         if (imag)
00806           {
00807             re = NDArray (dim_vector (nnz, 1));
00808             data = re.fortran_vec ();
00809           }
00810 
00811         tmp_pos = is.tellg ();
00812         read_mat5_binary_data (is, data, nnz, swap,
00813                                static_cast<enum mat5_data_type> (type), flt_fmt);
00814 
00815         if (! is || error_state)
00816           {
00817             error ("load: reading sparse matrix data for '%s'", retval.c_str ());
00818             goto data_read_error;
00819           }
00820 
00821         is.seekg (tmp_pos + static_cast<std::streamoff> (PAD (len)));
00822 
00823         // imaginary data subelement
00824         if (imag)
00825           {
00826             NDArray im (dim_vector (static_cast<int> (nnz), 1));
00827 
00828             if (read_mat5_tag (is, swap, type, len))
00829               {
00830                 error ("load: reading sparse matrix data for '%s'", retval.c_str ());
00831                 goto data_read_error;
00832               }
00833 
00834             read_mat5_binary_data (is, im.fortran_vec (), nnz, swap,
00835                                    static_cast<enum mat5_data_type> (type), flt_fmt);
00836 
00837             if (! is || error_state)
00838               {
00839                 error ("load: reading imaginary sparse matrix data for '%s'",
00840                        retval.c_str ());
00841                 goto data_read_error;
00842               }
00843 
00844             for (octave_idx_type i = 0; i < nnz; i++)
00845               scm.xdata (i) = Complex (re (i), im (i));
00846 
00847             tc = scm;
00848           }
00849         else
00850           tc = sm;
00851       }
00852       break;
00853 
00854     case MAT_FILE_FUNCTION_CLASS:
00855       {
00856         octave_value tc2;
00857         std::string nm
00858           = read_mat5_binary_element (is, filename, swap, global, tc2);
00859 
00860         if (! is || error_state)
00861           goto data_read_error;
00862 
00863         // Octave can handle both "/" and "\" as a directry seperator
00864         // and so can ignore the seperator field of m0. I think the
00865         // sentinel field is also save to ignore.
00866         Octave_map m0 = tc2.map_value();
00867         Octave_map m1 = m0.contents("function_handle")(0).map_value();
00868         std::string ftype = m1.contents("type")(0).string_value();
00869         std::string fname = m1.contents("function")(0).string_value();
00870         std::string fpath = m1.contents("file")(0).string_value();
00871 
00872         if (ftype == "simple" || ftype == "scopedfunction")
00873           {
00874             if (fpath.length() == 0)
00875               // We have a builtin function
00876               tc = make_fcn_handle (fname);
00877             else
00878               {
00879                 std::string mroot =
00880                   m0.contents("matlabroot")(0).string_value();
00881 
00882                 if ((fpath.length () >= mroot.length ()) &&
00883                     fpath.substr(0, mroot.length()) == mroot &&
00884                     OCTAVE_EXEC_PREFIX != mroot)
00885                   {
00886                     // If fpath starts with matlabroot, and matlabroot
00887                     // doesn't equal octave_config_info ("exec_prefix")
00888                     // then the function points to a version of Octave
00889                     // or Matlab other than the running version. In that
00890                     // case we replace with the same function in the
00891                     // running version of Octave?
00892 
00893                     // First check if just replacing matlabroot is enough
00894                     std::string str = OCTAVE_EXEC_PREFIX +
00895                       fpath.substr (mroot.length ());
00896                     file_stat fs (str);
00897 
00898                     if (fs.exists ())
00899                       {
00900                         size_t xpos
00901                           = str.find_last_of (file_ops::dir_sep_chars ());
00902 
00903                         std::string dir_name = str.substr (0, xpos);
00904 
00905                         octave_function *fcn
00906                           = load_fcn_from_file (str, dir_name, "", fname);
00907 
00908                         if (fcn)
00909                           {
00910                             octave_value tmp (fcn);
00911 
00912                             tc = octave_value (new octave_fcn_handle (tmp, fname));
00913                           }
00914                       }
00915                     else
00916                       {
00917                         // Next just search for it anywhere in the
00918                         // system path
00919                         string_vector names(3);
00920                         names(0) = fname + ".oct";
00921                         names(1) = fname + ".mex";
00922                         names(2) = fname + ".m";
00923 
00924                         dir_path p (load_path::system_path ());
00925 
00926                         str = octave_env::make_absolute (p.find_first_of (names));
00927 
00928                         size_t xpos
00929                           = str.find_last_of (file_ops::dir_sep_chars ());
00930 
00931                         std::string dir_name = str.substr (0, xpos);
00932 
00933                         octave_function *fcn
00934                           = load_fcn_from_file (str, dir_name, "", fname);
00935 
00936                         if (fcn)
00937                           {
00938                             octave_value tmp (fcn);
00939 
00940                             tc = octave_value (new octave_fcn_handle (tmp, fname));
00941                           }
00942                         else
00943                           {
00944                             warning ("load: can't find the file %s",
00945                                      fpath.c_str());
00946                             goto skip_ahead;
00947                           }
00948                       }
00949                   }
00950                 else
00951                   {
00952                     size_t xpos
00953                       = fpath.find_last_of (file_ops::dir_sep_chars ());
00954 
00955                     std::string dir_name = fpath.substr (0, xpos);
00956 
00957                     octave_function *fcn
00958                       = load_fcn_from_file (fpath, dir_name, "", fname);
00959 
00960                     if (fcn)
00961                       {
00962                         octave_value tmp (fcn);
00963 
00964                         tc = octave_value (new octave_fcn_handle (tmp, fname));
00965                       }
00966                     else
00967                       {
00968                         warning ("load: can't find the file %s",
00969                                  fpath.c_str());
00970                         goto skip_ahead;
00971                       }
00972                   }
00973               }
00974           }
00975         else if (ftype == "nested")
00976           {
00977             warning ("load: can't load nested function");
00978             goto skip_ahead;
00979           }
00980         else if (ftype == "anonymous")
00981           {
00982             Octave_map m2 = m1.contents("workspace")(0).map_value();
00983             uint32NDArray MCOS = m2.contents("MCOS")(0).uint32_array_value();
00984             octave_idx_type off = static_cast<octave_idx_type>(MCOS(4).double_value ());
00985             m2 = subsys_ov.map_value();
00986             m2 = m2.contents("MCOS")(0).map_value();
00987             tc2 = m2.contents("MCOS")(0).cell_value()(1 + off).cell_value()(1);
00988             m2 = tc2.map_value();
00989 
00990             unwind_protect_safe frame;
00991 
00992             // Set up temporary scope to use for evaluating the text
00993             // that defines the anonymous function.
00994 
00995             symbol_table::scope_id local_scope = symbol_table::alloc_scope ();
00996             frame.add_fcn (symbol_table::erase_scope, local_scope);
00997 
00998             symbol_table::set_scope (local_scope);
00999 
01000             octave_call_stack::push (local_scope, 0);
01001             frame.add_fcn (octave_call_stack::pop);
01002 
01003             if (m2.nfields() > 0)
01004               {
01005                 octave_value tmp;
01006 
01007                 for (Octave_map::iterator p0 = m2.begin() ;
01008                      p0 != m2.end(); p0++)
01009                   {
01010                     std::string key = m2.key(p0);
01011                     octave_value val = m2.contents(p0)(0);
01012 
01013                     symbol_table::varref (key, local_scope, 0) = val;
01014                   }
01015               }
01016 
01017             int parse_status;
01018             octave_value anon_fcn_handle =
01019               eval_string (fname.substr (4), true, parse_status);
01020 
01021             if (parse_status == 0)
01022               {
01023                 octave_fcn_handle *fh =
01024                   anon_fcn_handle.fcn_handle_value ();
01025 
01026                 if (fh)
01027                   tc = new octave_fcn_handle (fh->fcn_val (), "@<anonymous>");
01028                 else
01029                   {
01030                     error ("load: failed to load anonymous function handle");
01031                     goto skip_ahead;
01032                   }
01033               }
01034             else
01035               {
01036                 error ("load: failed to load anonymous function handle");
01037                 goto skip_ahead;
01038               }
01039 
01040             frame.run ();
01041           }
01042         else
01043           {
01044             error ("load: invalid function handle type");
01045             goto skip_ahead;
01046           }
01047       }
01048       break;
01049 
01050     case MAT_FILE_WORKSPACE_CLASS:
01051       {
01052         Octave_map m (dim_vector (1, 1));
01053         int n_fields = 2;
01054         string_vector field (n_fields);
01055 
01056         for (int i = 0; i < n_fields; i++)
01057           {
01058             int32_t fn_type;
01059             int32_t fn_len;
01060             if (read_mat5_tag (is, swap, fn_type, fn_len) || fn_type != miINT8)
01061               {
01062                 error ("load: invalid field name subelement");
01063                 goto data_read_error;
01064               }
01065 
01066             OCTAVE_LOCAL_BUFFER (char, elname, fn_len + 1);
01067 
01068             std::streampos tmp_pos = is.tellg ();
01069 
01070             if (fn_len)
01071               {
01072                 if (! is.read (elname, fn_len))
01073                   goto data_read_error;
01074 
01075                 is.seekg (tmp_pos +
01076                           static_cast<std::streamoff> (PAD (fn_len)));
01077               }
01078 
01079             elname[fn_len] = '\0';
01080 
01081             field(i) = elname;
01082           }
01083 
01084         std::vector<Cell> elt (n_fields);
01085 
01086         for (octave_idx_type i = 0; i < n_fields; i++)
01087           elt[i] = Cell (dims);
01088 
01089         octave_idx_type n = dims.numel ();
01090 
01091         // fields subelements
01092         for (octave_idx_type j = 0; j < n; j++)
01093           {
01094             for (octave_idx_type i = 0; i < n_fields; i++)
01095               {
01096                 if (field(i) == "MCOS")
01097                   {
01098                     octave_value fieldtc;
01099                     read_mat5_binary_element (is, filename, swap, global,
01100                                               fieldtc);
01101                     if (! is || error_state)
01102                       goto data_read_error;
01103 
01104                     elt[i](j) = fieldtc;
01105                   }
01106                 else
01107                   elt[i](j) = octave_value ();
01108               }
01109           }
01110 
01111         for (octave_idx_type i = 0; i < n_fields; i++)
01112           m.assign (field (i), elt[i]);
01113         tc = m;
01114       }
01115       break;
01116 
01117     case MAT_FILE_OBJECT_CLASS:
01118       {
01119         isclass = true;
01120 
01121         if (read_mat5_tag (is, swap, type, len) || type != miINT8)
01122           {
01123             error ("load: invalid class name");
01124             goto skip_ahead;
01125           }
01126 
01127         {
01128           OCTAVE_LOCAL_BUFFER (char, name, len+1);
01129 
01130           std::streampos tmp_pos = is.tellg ();
01131 
01132           if (len)
01133             {
01134               if (! is.read (name, len ))
01135                 goto data_read_error;
01136 
01137               is.seekg (tmp_pos + static_cast<std::streamoff> (PAD (len)));
01138             }
01139 
01140           name[len] = '\0';
01141           classname = name;
01142         }
01143       }
01144       // Fall-through
01145     case MAT_FILE_STRUCT_CLASS:
01146       {
01147         Octave_map m (dim_vector (1, 1));
01148         int32_t fn_type;
01149         int32_t fn_len;
01150         int32_t field_name_length;
01151 
01152         // field name length subelement -- actually the maximum length
01153         // of a field name.  The Matlab docs promise this will always
01154         // be 32.  We read and use the actual value, on the theory
01155         // that eventually someone will recognize that's a waste of
01156         // space.
01157         if (read_mat5_tag (is, swap, fn_type, fn_len) || fn_type != miINT32)
01158           {
01159             error ("load: invalid field name length subelement");
01160             goto data_read_error;
01161           }
01162 
01163         if (! is.read (reinterpret_cast<char *> (&field_name_length), fn_len ))
01164           goto data_read_error;
01165 
01166         if (swap)
01167           swap_bytes<4> (&field_name_length);
01168 
01169         // field name subelement.  The length of this subelement tells
01170         // us how many fields there are.
01171         if (read_mat5_tag (is, swap, fn_type, fn_len) || fn_type != miINT8)
01172           {
01173             error ("load: invalid field name subelement");
01174             goto data_read_error;
01175           }
01176 
01177         octave_idx_type n_fields = fn_len/field_name_length;
01178 
01179         if (n_fields > 0)
01180           {
01181             fn_len = PAD (fn_len);
01182 
01183             OCTAVE_LOCAL_BUFFER (char, elname, fn_len);
01184 
01185             if (! is.read (elname, fn_len))
01186               goto data_read_error;
01187 
01188             std::vector<Cell> elt (n_fields);
01189 
01190             for (octave_idx_type i = 0; i < n_fields; i++)
01191               elt[i] = Cell (dims);
01192 
01193             octave_idx_type n = dims.numel ();
01194 
01195             // fields subelements
01196             for (octave_idx_type j = 0; j < n; j++)
01197               {
01198                 for (octave_idx_type i = 0; i < n_fields; i++)
01199                   {
01200                     octave_value fieldtc;
01201                     read_mat5_binary_element (is, filename, swap, global,
01202                                               fieldtc);
01203                     elt[i](j) = fieldtc;
01204                   }
01205               }
01206 
01207             for (octave_idx_type i = 0; i < n_fields; i++)
01208               {
01209                 const char *key = elname + i*field_name_length;
01210 
01211                 m.assign (key, elt[i]);
01212               }
01213           }
01214 
01215         if (isclass)
01216           {
01217             if (classname == "inline")
01218               {
01219                 // inline is not an object in Octave but rather an
01220                 // overload of a function handle. Special case.
01221                 tc =
01222                   new octave_fcn_inline (m.contents("expr")(0).string_value(),
01223                                          m.contents("args")(0).string_value());
01224               }
01225             else
01226               {
01227                 octave_class* cls
01228                   = new octave_class (m, classname,
01229                                       std::list<std::string> ());
01230 
01231                 if (cls->reconstruct_exemplar ())
01232                   {
01233 
01234                     if (! cls->reconstruct_parents ())
01235                       warning ("load: unable to reconstruct object inheritance");
01236 
01237                     tc = cls;
01238                     if (load_path::find_method (classname, "loadobj") !=
01239                         std::string())
01240                       {
01241                         octave_value_list tmp = feval ("loadobj", tc, 1);
01242 
01243                         if (! error_state)
01244                           tc = tmp(0);
01245                         else
01246                           goto data_read_error;
01247                       }
01248                   }
01249                 else
01250                   {
01251                     tc = m;
01252                     warning ("load: element has been converted to a structure");
01253                   }
01254               }
01255           }
01256         else
01257           tc = m;
01258       }
01259       break;
01260 
01261     case MAT_FILE_INT8_CLASS:
01262       OCTAVE_MAT5_INTEGER_READ (int8NDArray);
01263       break;
01264 
01265     case MAT_FILE_UINT8_CLASS:
01266       {
01267         OCTAVE_MAT5_INTEGER_READ (uint8NDArray);
01268 
01269         // Logical variables can either be MAT_FILE_UINT8_CLASS or
01270         // MAT_FILE_DOUBLE_CLASS, so check if we have a logical
01271         // variable and convert it.
01272 
01273         if (logicalvar)
01274           {
01275             uint8NDArray in = tc.uint8_array_value ();
01276             octave_idx_type nel = in.numel ();
01277             boolNDArray out (dims);
01278 
01279             for (octave_idx_type i = 0; i < nel; i++)
01280               out (i) = in(i).bool_value ();
01281 
01282             tc = out;
01283           }
01284       }
01285       break;
01286 
01287     case MAT_FILE_INT16_CLASS:
01288       OCTAVE_MAT5_INTEGER_READ (int16NDArray);
01289       break;
01290 
01291     case MAT_FILE_UINT16_CLASS:
01292       OCTAVE_MAT5_INTEGER_READ (uint16NDArray);
01293       break;
01294 
01295     case MAT_FILE_INT32_CLASS:
01296       OCTAVE_MAT5_INTEGER_READ (int32NDArray);
01297       break;
01298 
01299     case MAT_FILE_UINT32_CLASS:
01300       OCTAVE_MAT5_INTEGER_READ (uint32NDArray);
01301       break;
01302 
01303     case MAT_FILE_INT64_CLASS:
01304       OCTAVE_MAT5_INTEGER_READ (int64NDArray);
01305       break;
01306 
01307     case MAT_FILE_UINT64_CLASS:
01308       OCTAVE_MAT5_INTEGER_READ (uint64NDArray);
01309       break;
01310 
01311 
01312     case MAT_FILE_SINGLE_CLASS:
01313       {
01314         FloatNDArray re (dims);
01315 
01316         // real data subelement
01317 
01318         std::streampos tmp_pos;
01319 
01320         if (read_mat5_tag (is, swap, type, len))
01321           {
01322             error ("load: reading matrix data for '%s'", retval.c_str ());
01323             goto data_read_error;
01324           }
01325 
01326         octave_idx_type n = re.numel ();
01327         tmp_pos = is.tellg ();
01328         read_mat5_binary_data (is, re.fortran_vec (), n, swap,
01329                                static_cast<enum mat5_data_type> (type), flt_fmt);
01330 
01331         if (! is || error_state)
01332           {
01333             error ("load: reading matrix data for '%s'", retval.c_str ());
01334             goto data_read_error;
01335           }
01336 
01337         is.seekg (tmp_pos + static_cast<std::streamoff> (PAD (len)));
01338 
01339         if (imag)
01340           {
01341             // imaginary data subelement
01342 
01343             FloatNDArray im (dims);
01344 
01345             if (read_mat5_tag (is, swap, type, len))
01346               {
01347                 error ("load: reading matrix data for '%s'", retval.c_str ());
01348                 goto data_read_error;
01349               }
01350 
01351             n = im.numel ();
01352             read_mat5_binary_data (is, im.fortran_vec (), n, swap,
01353                                    static_cast<enum mat5_data_type> (type), flt_fmt);
01354 
01355             if (! is || error_state)
01356               {
01357                 error ("load: reading imaginary matrix data for '%s'",
01358                        retval.c_str ());
01359                 goto data_read_error;
01360               }
01361 
01362             FloatComplexNDArray ctmp (dims);
01363 
01364             for (octave_idx_type i = 0; i < n; i++)
01365               ctmp(i) = FloatComplex (re(i), im(i));
01366 
01367             tc = ctmp;
01368           }
01369         else
01370           tc = re;
01371       }
01372       break;
01373 
01374     case MAT_FILE_CHAR_CLASS:
01375       // handle as a numerical array to start with
01376 
01377     case MAT_FILE_DOUBLE_CLASS:
01378     default:
01379       {
01380         NDArray re (dims);
01381 
01382         // real data subelement
01383 
01384         std::streampos tmp_pos;
01385 
01386         if (read_mat5_tag (is, swap, type, len))
01387           {
01388             error ("load: reading matrix data for '%s'", retval.c_str ());
01389             goto data_read_error;
01390           }
01391 
01392         octave_idx_type n = re.numel ();
01393         tmp_pos = is.tellg ();
01394         read_mat5_binary_data (is, re.fortran_vec (), n, swap,
01395                                static_cast<enum mat5_data_type> (type), flt_fmt);
01396 
01397         if (! is || error_state)
01398           {
01399             error ("load: reading matrix data for '%s'", retval.c_str ());
01400             goto data_read_error;
01401           }
01402 
01403         is.seekg (tmp_pos + static_cast<std::streamoff> (PAD (len)));
01404 
01405         if (logicalvar)
01406           {
01407             // Logical variables can either be MAT_FILE_UINT8_CLASS or
01408             // MAT_FILE_DOUBLE_CLASS, so check if we have a logical
01409             // variable and convert it.
01410 
01411             boolNDArray out (dims);
01412 
01413             for (octave_idx_type i = 0; i < n; i++)
01414               out (i) = static_cast<bool> (re (i));
01415 
01416             tc = out;
01417           }
01418         else if (imag)
01419           {
01420             // imaginary data subelement
01421 
01422             NDArray im (dims);
01423 
01424             if (read_mat5_tag (is, swap, type, len))
01425               {
01426                 error ("load: reading matrix data for '%s'", retval.c_str ());
01427                 goto data_read_error;
01428               }
01429 
01430             n = im.numel ();
01431             read_mat5_binary_data (is, im.fortran_vec (), n, swap,
01432                                    static_cast<enum mat5_data_type> (type), flt_fmt);
01433 
01434             if (! is || error_state)
01435               {
01436                 error ("load: reading imaginary matrix data for '%s'",
01437                        retval.c_str ());
01438                 goto data_read_error;
01439               }
01440 
01441             ComplexNDArray ctmp (dims);
01442 
01443             for (octave_idx_type i = 0; i < n; i++)
01444               ctmp(i) = Complex (re(i), im(i));
01445 
01446             tc = ctmp;
01447           }
01448         else
01449           {
01450             if (arrayclass == MAT_FILE_CHAR_CLASS)
01451               {
01452                 if (type == miUTF16 || type == miUTF32)
01453                   {
01454                     bool found_big_char = false;
01455                     for (octave_idx_type i = 0; i < n; i++)
01456                       {
01457                         if (re(i) > 127) {
01458                           re(i) = '?';
01459                           found_big_char = true;
01460                         }
01461                       }
01462 
01463                     if (found_big_char)
01464                       warning ("load: can not read non-ASCII portions of UTF characters; replacing unreadable characters with '?'");
01465                   }
01466                 else if (type == miUTF8)
01467                   {
01468                     // Search for multi-byte encoded UTF8 characters and
01469                     // replace with 0x3F for '?'... Give the user a warning
01470 
01471                     bool utf8_multi_byte = false;
01472                     for (octave_idx_type i = 0; i < n; i++)
01473                       {
01474                         unsigned char a = static_cast<unsigned char> (re(i));
01475                         if (a > 0x7f)
01476                           utf8_multi_byte = true;
01477                       }
01478 
01479                     if (utf8_multi_byte)
01480                       {
01481                         warning ("load: can not read multi-byte encoded UTF8 characters; replacing unreadable characters with '?'");
01482                         for (octave_idx_type i = 0; i < n; i++)
01483                           {
01484                             unsigned char a = static_cast<unsigned char> (re(i));
01485                             if (a > 0x7f)
01486                               re(i) = '?';
01487                           }
01488                       }
01489                   }
01490                 tc = re;
01491                 tc = tc.convert_to_str (false, true, '\'');
01492               }
01493             else
01494               tc = re;
01495           }
01496       }
01497     }
01498 
01499   is.seekg (pos + static_cast<std::streamoff> (element_length));
01500 
01501   if (is.eof ())
01502     is.clear ();
01503 
01504   return retval;
01505 
01506  data_read_error:
01507  early_read_error:
01508   error ("load: trouble reading binary file '%s'", filename.c_str ());
01509   return std::string ();
01510 
01511  skip_ahead:
01512   warning ("skipping over '%s'", retval.c_str ());
01513   is.seekg (pos + static_cast<std::streamoff> (element_length));
01514   return read_mat5_binary_element (is, filename, swap, global, tc);
01515 }
01516 
01517 int
01518 read_mat5_binary_file_header (std::istream& is, bool& swap, bool quiet,
01519                               const std::string& filename)
01520 {
01521   int16_t version=0, magic=0;
01522   uint64_t subsys_offset;
01523 
01524   is.seekg (116, std::ios::beg);
01525   is.read (reinterpret_cast<char *> (&subsys_offset), 8);
01526 
01527   is.seekg (124, std::ios::beg);
01528   is.read (reinterpret_cast<char *> (&version), 2);
01529   is.read (reinterpret_cast<char *> (&magic), 2);
01530 
01531   if (magic == 0x4d49)
01532     swap = 0;
01533   else if (magic == 0x494d)
01534     swap = 1;
01535   else
01536     {
01537       if (! quiet)
01538         error ("load: can't read binary file");
01539       return -1;
01540     }
01541 
01542   if (! swap)                   // version number is inverse swapped!
01543     version = ((version >> 8) & 0xff) + ((version & 0xff) << 8);
01544 
01545   if (version != 1 && !quiet)
01546     warning ("load: found version %d binary MAT file, "
01547              "but only prepared for version 1", version);
01548 
01549   if (swap)
01550     swap_bytes<8> (&subsys_offset, 1);
01551 
01552   if (subsys_offset != 0x2020202020202020ULL && subsys_offset != 0ULL)
01553     {
01554       // Read the subsystem data block
01555       is.seekg (subsys_offset, std::ios::beg);
01556 
01557       octave_value tc;
01558       bool global;
01559       read_mat5_binary_element (is, filename, swap, global, tc);
01560 
01561       if (!is || error_state)
01562         return -1;
01563 
01564       if (tc.is_uint8_type ())
01565         {
01566           const uint8NDArray itmp = tc.uint8_array_value();
01567           octave_idx_type ilen = itmp.numel ();
01568 
01569           // Why should I have to initialize outbuf as just overwrite
01570           std::string outbuf (ilen - 7, ' ');
01571 
01572           // FIXME -- find a way to avoid casting away const here
01573           char *ctmp = const_cast<char *> (outbuf.c_str ());
01574           for (octave_idx_type j = 8; j < ilen; j++)
01575             ctmp[j-8] = itmp(j).char_value ();
01576 
01577           std::istringstream fh_ws (outbuf);
01578 
01579           read_mat5_binary_element (fh_ws, filename, swap, global, subsys_ov);
01580 
01581           if (error_state)
01582             return -1;
01583         }
01584       else
01585         return -1;
01586 
01587       // Reposition to just after the header
01588       is.seekg (128, std::ios::beg);
01589     }
01590 
01591   return 0;
01592 }
01593 
01594 static int
01595 write_mat5_tag (std::ostream& is, int type, octave_idx_type bytes)
01596 {
01597   int32_t temp;
01598 
01599   if (bytes > 0 && bytes <= 4)
01600     temp = (bytes << 16) + type;
01601   else
01602     {
01603       temp = type;
01604       if (! is.write (reinterpret_cast<char *> (&temp), 4))
01605         goto data_write_error;
01606       temp = bytes;
01607     }
01608 
01609   if (! is.write (reinterpret_cast<char *> (&temp), 4))
01610     goto data_write_error;
01611 
01612   return 0;
01613 
01614  data_write_error:
01615   return 1;
01616 }
01617 
01618 // Have to use copy here to avoid writing over data accessed via
01619 // Matrix::data().
01620 
01621 #define MAT5_DO_WRITE(TYPE, data, count, stream) \
01622   do \
01623     { \
01624       OCTAVE_LOCAL_BUFFER (TYPE, ptr, count); \
01625       for (octave_idx_type i = 0; i < count; i++) \
01626         ptr[i] = static_cast<TYPE> (data[i]); \
01627       stream.write (reinterpret_cast<char *> (ptr), count * sizeof (TYPE)); \
01628     } \
01629   while (0)
01630 
01631 // write out the numeric values in M to OS,
01632 // preceded by the appropriate tag.
01633 static void
01634 write_mat5_array (std::ostream& os, const NDArray& m, bool save_as_floats)
01635 {
01636   save_type st = LS_DOUBLE;
01637   const double *data = m.data ();
01638 
01639   if (save_as_floats)
01640     {
01641       if (m.too_large_for_float ())
01642         {
01643           warning ("save: some values too large to save as floats --");
01644           warning ("save: saving as doubles instead");
01645         }
01646       else
01647         st = LS_FLOAT;
01648     }
01649 
01650   double max_val, min_val;
01651   if (m.all_integers (max_val, min_val))
01652     st = get_save_type (max_val, min_val);
01653 
01654   mat5_data_type mst;
01655   int size;
01656   switch (st)
01657     {
01658     default:
01659     case LS_DOUBLE:  mst = miDOUBLE; size = 8; break;
01660     case LS_FLOAT:   mst = miSINGLE; size = 4; break;
01661     case LS_U_CHAR:  mst = miUINT8;  size = 1; break;
01662     case LS_U_SHORT: mst = miUINT16; size = 2; break;
01663     case LS_U_INT:   mst = miUINT32; size = 4; break;
01664     case LS_CHAR:    mst = miINT8;   size = 1; break;
01665     case LS_SHORT:   mst = miINT16;  size = 2; break;
01666     case LS_INT:     mst = miINT32;  size = 4; break;
01667     }
01668 
01669   octave_idx_type nel = m.numel ();
01670   octave_idx_type len = nel*size;
01671 
01672   write_mat5_tag (os, mst, len);
01673 
01674   {
01675     switch (st)
01676       {
01677       case LS_U_CHAR:
01678         MAT5_DO_WRITE (uint8_t, data, nel, os);
01679         break;
01680 
01681       case LS_U_SHORT:
01682         MAT5_DO_WRITE (uint16_t, data, nel, os);
01683         break;
01684 
01685       case LS_U_INT:
01686         MAT5_DO_WRITE (uint32_t, data, nel, os);
01687         break;
01688 
01689       case LS_U_LONG:
01690         MAT5_DO_WRITE (uint64_t, data, nel, os);
01691         break;
01692 
01693       case LS_CHAR:
01694         MAT5_DO_WRITE (int8_t, data, nel, os);
01695         break;
01696 
01697       case LS_SHORT:
01698         MAT5_DO_WRITE (int16_t, data, nel, os);
01699         break;
01700 
01701       case LS_INT:
01702         MAT5_DO_WRITE (int32_t, data, nel, os);
01703         break;
01704 
01705       case LS_LONG:
01706         MAT5_DO_WRITE (int64_t, data, nel, os);
01707         break;
01708 
01709       case LS_FLOAT:
01710         MAT5_DO_WRITE (float, data, nel, os);
01711         break;
01712 
01713       case LS_DOUBLE: // No conversion necessary.
01714         os.write (reinterpret_cast<const char *> (data), len);
01715         break;
01716 
01717       default:
01718         (*current_liboctave_error_handler)
01719           ("unrecognized data format requested");
01720         break;
01721       }
01722   }
01723   if (PAD (len) > len)
01724     {
01725       static char buf[9]="\x00\x00\x00\x00\x00\x00\x00\x00";
01726       os.write (buf, PAD (len) - len);
01727     }
01728 }
01729 
01730 static void
01731 write_mat5_array (std::ostream& os, const FloatNDArray& m, bool)
01732 {
01733   save_type st = LS_FLOAT;
01734   const float *data = m.data ();
01735 
01736   float max_val, min_val;
01737   if (m.all_integers (max_val, min_val))
01738     st = get_save_type (max_val, min_val);
01739 
01740   mat5_data_type mst;
01741   int size;
01742   switch (st)
01743     {
01744     default:
01745     case LS_DOUBLE:  mst = miDOUBLE; size = 8; break;
01746     case LS_FLOAT:   mst = miSINGLE; size = 4; break;
01747     case LS_U_CHAR:  mst = miUINT8;  size = 1; break;
01748     case LS_U_SHORT: mst = miUINT16; size = 2; break;
01749     case LS_U_INT:   mst = miUINT32; size = 4; break;
01750     case LS_CHAR:    mst = miINT8;   size = 1; break;
01751     case LS_SHORT:   mst = miINT16;  size = 2; break;
01752     case LS_INT:     mst = miINT32;  size = 4; break;
01753     }
01754 
01755   octave_idx_type nel = m.numel ();
01756   octave_idx_type len = nel*size;
01757 
01758   write_mat5_tag (os, mst, len);
01759 
01760   {
01761     switch (st)
01762       {
01763       case LS_U_CHAR:
01764         MAT5_DO_WRITE (uint8_t, data, nel, os);
01765         break;
01766 
01767       case LS_U_SHORT:
01768         MAT5_DO_WRITE (uint16_t, data, nel, os);
01769         break;
01770 
01771       case LS_U_INT:
01772         MAT5_DO_WRITE (uint32_t, data, nel, os);
01773         break;
01774 
01775       case LS_U_LONG:
01776         MAT5_DO_WRITE (uint64_t, data, nel, os);
01777         break;
01778 
01779       case LS_CHAR:
01780         MAT5_DO_WRITE (int8_t, data, nel, os);
01781         break;
01782 
01783       case LS_SHORT:
01784         MAT5_DO_WRITE (int16_t, data, nel, os);
01785         break;
01786 
01787       case LS_INT:
01788         MAT5_DO_WRITE (int32_t, data, nel, os);
01789         break;
01790 
01791       case LS_LONG:
01792         MAT5_DO_WRITE (int64_t, data, nel, os);
01793         break;
01794 
01795       case LS_FLOAT: // No conversion necessary.
01796         os.write (reinterpret_cast<const char *> (data), len);
01797         break;
01798 
01799       case LS_DOUBLE:
01800         MAT5_DO_WRITE (double, data, nel, os);
01801         break;
01802 
01803       default:
01804         (*current_liboctave_error_handler)
01805           ("unrecognized data format requested");
01806         break;
01807       }
01808   }
01809   if (PAD (len) > len)
01810     {
01811       static char buf[9]="\x00\x00\x00\x00\x00\x00\x00\x00";
01812       os.write (buf, PAD (len) - len);
01813     }
01814 }
01815 
01816 template <class T>
01817 void
01818 write_mat5_integer_data (std::ostream& os, const T *m, int size,
01819                          octave_idx_type nel)
01820 {
01821   mat5_data_type mst;
01822   unsigned len;
01823 
01824   switch (size)
01825     {
01826     case 1:
01827       mst = miUINT8;
01828       break;
01829     case 2:
01830       mst = miUINT16;
01831       break;
01832     case 4:
01833       mst = miUINT32;
01834       break;
01835     case 8:
01836       mst = miUINT64;
01837       break;
01838     case -1:
01839       mst = miINT8;
01840       size = - size;
01841       break;
01842     case -2:
01843       mst = miINT16;
01844       size = - size;
01845       break;
01846     case -4:
01847       mst = miINT32;
01848       size = - size;
01849       break;
01850     case -8:
01851     default:
01852       mst = miINT64;
01853       size = - size;
01854       break;
01855     }
01856 
01857   len = nel*size;
01858   write_mat5_tag (os, mst, len);
01859 
01860   os.write (reinterpret_cast<const char *> (m), len);
01861 
01862   if (PAD (len) > len)
01863     {
01864       static char buf[9]="\x00\x00\x00\x00\x00\x00\x00\x00";
01865       os.write (buf, PAD (len) - len);
01866     }
01867 }
01868 
01869 template void
01870 write_mat5_integer_data (std::ostream& os, const octave_int8 *m,
01871                          int size, octave_idx_type nel);
01872 
01873 template void
01874 write_mat5_integer_data (std::ostream& os, const octave_int16 *m,
01875                          int size, octave_idx_type nel);
01876 
01877 template void
01878 write_mat5_integer_data (std::ostream& os, const octave_int32 *m,
01879                          int size, octave_idx_type nel);
01880 
01881 template void
01882 write_mat5_integer_data (std::ostream& os, const octave_int64 *m,
01883                          int size, octave_idx_type nel);
01884 
01885 template void
01886 write_mat5_integer_data (std::ostream& os, const octave_uint8 *m,
01887                          int size, octave_idx_type nel);
01888 
01889 template void
01890 write_mat5_integer_data (std::ostream& os, const octave_uint16 *m,
01891                          int size, octave_idx_type nel);
01892 
01893 template void
01894 write_mat5_integer_data (std::ostream& os, const octave_uint32 *m,
01895                          int size, octave_idx_type nel);
01896 
01897 template void
01898 write_mat5_integer_data (std::ostream& os, const octave_uint64 *m,
01899                          int size, octave_idx_type nel);
01900 
01901 template void
01902 write_mat5_integer_data (std::ostream& os, const int *m,
01903                          int size, octave_idx_type nel);
01904 
01905 // Write out cell element values in the cell array to OS, preceded by
01906 // the appropriate tag.
01907 
01908 static bool
01909 write_mat5_cell_array (std::ostream& os, const Cell& cell,
01910                        bool mark_as_global, bool save_as_floats)
01911 {
01912   octave_idx_type nel = cell.numel ();
01913 
01914   for (octave_idx_type i = 0; i < nel; i++)
01915     {
01916       octave_value ov = cell(i);
01917 
01918       if (! save_mat5_binary_element (os, ov, "", mark_as_global,
01919                                       false, save_as_floats))
01920         return false;
01921     }
01922 
01923   return true;
01924 }
01925 
01926 int
01927 save_mat5_array_length (const double* val, octave_idx_type nel,
01928                         bool save_as_floats)
01929 {
01930   if (nel > 0)
01931     {
01932       int size = 8;
01933 
01934       if (save_as_floats)
01935         {
01936           bool too_large_for_float = false;
01937           for (octave_idx_type i = 0; i < nel; i++)
01938             {
01939               double tmp = val [i];
01940 
01941               if (! (xisnan (tmp) || xisinf (tmp))
01942                   && fabs (tmp) > FLT_MAX)
01943                 {
01944                   too_large_for_float = true;
01945                   break;
01946                 }
01947             }
01948 
01949           if (!too_large_for_float)
01950             size = 4;
01951         }
01952 
01953       // The code below is disabled since get_save_type currently doesn't
01954       // deal with integer types. This will need to be activated if get_save_type
01955       // is changed.
01956 
01957       // double max_val = val[0];
01958       // double min_val = val[0];
01959       // bool all_integers =  true;
01960       //
01961       // for (int i = 0; i < nel; i++)
01962       //   {
01963       //     double val = val[i];
01964       //
01965       //     if (val > max_val)
01966       //       max_val = val;
01967       //
01968       //     if (val < min_val)
01969       //       min_val = val;
01970       //
01971       //     if (D_NINT (val) != val)
01972       //       {
01973       //         all_integers = false;
01974       //         break;
01975       //       }
01976       //   }
01977       //
01978       // if (all_integers)
01979       //   {
01980       //     if (max_val < 256 && min_val > -1)
01981       //       size = 1;
01982       //     else if (max_val < 65536 && min_val > -1)
01983       //       size = 2;
01984       //     else if (max_val < 4294967295UL && min_val > -1)
01985       //       size = 4;
01986       //     else if (max_val < 128 && min_val >= -128)
01987       //       size = 1;
01988       //     else if (max_val < 32768 && min_val >= -32768)
01989       //       size = 2;
01990       //     else if (max_val <= 2147483647L && min_val >= -2147483647L)
01991       //       size = 4;
01992       //   }
01993 
01994       return 8 + nel * size;
01995     }
01996   else
01997     return 8;
01998 }
01999 
02000 int
02001 save_mat5_array_length (const float* /* val */, octave_idx_type nel, bool)
02002 {
02003   if (nel > 0)
02004     {
02005       int size = 4;
02006 
02007 
02008       // The code below is disabled since get_save_type currently doesn't
02009       // deal with integer types. This will need to be activated if get_save_type
02010       // is changed.
02011 
02012       // float max_val = val[0];
02013       // float min_val = val[0];
02014       // bool all_integers =  true;
02015       //
02016       // for (int i = 0; i < nel; i++)
02017       //   {
02018       //     float val = val[i];
02019       //
02020       //     if (val > max_val)
02021       //       max_val = val;
02022       //
02023       //     if (val < min_val)
02024       //       min_val = val;
02025       //
02026       //     if (D_NINT (val) != val)
02027       //       {
02028       //         all_integers = false;
02029       //         break;
02030       //       }
02031       //   }
02032       //
02033       // if (all_integers)
02034       //   {
02035       //     if (max_val < 256 && min_val > -1)
02036       //       size = 1;
02037       //     else if (max_val < 65536 && min_val > -1)
02038       //       size = 2;
02039       //     else if (max_val < 4294967295UL && min_val > -1)
02040       //       size = 4;
02041       //     else if (max_val < 128 && min_val >= -128)
02042       //       size = 1;
02043       //     else if (max_val < 32768 && min_val >= -32768)
02044       //       size = 2;
02045       //     else if (max_val <= 2147483647L && min_val >= -2147483647L)
02046       //       size = 4;
02047       //   }
02048 
02049       // Round nel up to nearest even number of elements. Take into account
02050       // Short tags for 4 byte elements.
02051       return PAD ((nel > 0 && nel * size <= 4 ? 4 : 8) + nel * size);
02052     }
02053   else
02054     return 8;
02055 }
02056 
02057 int
02058 save_mat5_array_length (const Complex* val, octave_idx_type nel,
02059                         bool save_as_floats)
02060 {
02061   int ret;
02062 
02063   OCTAVE_LOCAL_BUFFER (double, tmp, nel);
02064 
02065   for (octave_idx_type i = 1; i < nel; i++)
02066     tmp[i] = std::real (val[i]);
02067 
02068   ret = save_mat5_array_length (tmp, nel, save_as_floats);
02069 
02070   for (octave_idx_type i = 1; i < nel; i++)
02071     tmp[i] = std::imag (val[i]);
02072 
02073   ret += save_mat5_array_length (tmp, nel, save_as_floats);
02074 
02075   return ret;
02076 }
02077 
02078 int
02079 save_mat5_array_length (const FloatComplex* val, octave_idx_type nel,
02080                         bool save_as_floats)
02081 {
02082   int ret;
02083 
02084   OCTAVE_LOCAL_BUFFER (float, tmp, nel);
02085 
02086   for (octave_idx_type i = 1; i < nel; i++)
02087     tmp[i] = std::real (val[i]);
02088 
02089   ret = save_mat5_array_length (tmp, nel, save_as_floats);
02090 
02091   for (octave_idx_type i = 1; i < nel; i++)
02092     tmp[i] = std::imag (val[i]);
02093 
02094   ret += save_mat5_array_length (tmp, nel, save_as_floats);
02095 
02096   return ret;
02097 }
02098 
02099 int
02100 save_mat5_element_length (const octave_value& tc, const std::string& name,
02101                           bool save_as_floats, bool mat7_format)
02102 {
02103   size_t max_namelen = (mat7_format ? 63 : 31);
02104   size_t len = name.length ();
02105   std::string cname = tc.class_name ();
02106   int ret = 32;
02107 
02108   if (len > 4)
02109     ret += PAD (len > max_namelen ? max_namelen : len);
02110 
02111   ret += PAD (4 * tc.ndims ());
02112 
02113   if (tc.is_string ())
02114     {
02115       charNDArray chm = tc.char_array_value ();
02116       ret += 8;
02117       if (chm.numel () > 2)
02118         ret += PAD (2 * chm.numel ());
02119     }
02120   else if (tc.is_sparse_type ())
02121     {
02122       if (tc.is_complex_type ())
02123         {
02124           const SparseComplexMatrix m = tc.sparse_complex_matrix_value ();
02125           octave_idx_type nc = m.cols ();
02126           octave_idx_type nnz = m.nnz ();
02127 
02128           ret += 16 + save_mat5_array_length (m.data (), nnz, save_as_floats);
02129           if (nnz > 1)
02130             ret += PAD (nnz * sizeof (int32_t));
02131           if (nc > 0)
02132             ret += PAD ((nc + 1) * sizeof (int32_t));
02133         }
02134       else
02135         {
02136           const SparseMatrix m = tc.sparse_matrix_value ();
02137           octave_idx_type nc = m.cols ();
02138           octave_idx_type nnz = m.nnz ();
02139 
02140           ret += 16 + save_mat5_array_length (m.data (), nnz, save_as_floats);
02141           if (nnz > 1)
02142             ret += PAD (nnz * sizeof (int32_t));
02143           if (nc > 0)
02144             ret += PAD ((nc + 1) * sizeof (int32_t));
02145         }
02146     }
02147 
02148 #define INT_LEN(nel, size) \
02149   { \
02150     ret += 8; \
02151     octave_idx_type sz = nel * size; \
02152     if (sz > 4) \
02153       ret += PAD (sz);  \
02154   }
02155 
02156   else if (cname == "int8")
02157     INT_LEN (tc.int8_array_value ().numel (), 1)
02158   else if (cname == "int16")
02159     INT_LEN (tc.int16_array_value ().numel (), 2)
02160   else if (cname == "int32")
02161     INT_LEN (tc.int32_array_value ().numel (), 4)
02162   else if (cname == "int64")
02163     INT_LEN (tc.int64_array_value ().numel (), 8)
02164   else if (cname == "uint8")
02165     INT_LEN (tc.uint8_array_value ().numel (), 1)
02166   else if (cname == "uint16")
02167     INT_LEN (tc.uint16_array_value ().numel (), 2)
02168   else if (cname == "uint32")
02169     INT_LEN (tc.uint32_array_value ().numel (), 4)
02170   else if (cname == "uint64")
02171     INT_LEN (tc.uint64_array_value ().numel (), 8)
02172   else if (tc.is_bool_type ())
02173     INT_LEN (tc.bool_array_value ().numel (), 1)
02174   else if (tc.is_real_scalar () || tc.is_real_matrix () || tc.is_range ())
02175     {
02176       if (tc.is_single_type ())
02177         {
02178           const FloatNDArray m = tc.float_array_value ();
02179           ret += save_mat5_array_length (m.fortran_vec (), m.numel (),
02180                                          save_as_floats);
02181         }
02182       else
02183         {
02184           const NDArray m = tc.array_value ();
02185           ret += save_mat5_array_length (m.fortran_vec (), m.numel (),
02186                                          save_as_floats);
02187         }
02188     }
02189   else if (tc.is_cell ())
02190     {
02191       Cell cell = tc.cell_value ();
02192       octave_idx_type nel = cell.numel ();
02193 
02194       for (int i = 0; i < nel; i++)
02195         ret += 8 +
02196           save_mat5_element_length (cell (i), "", save_as_floats, mat7_format);
02197     }
02198   else if (tc.is_complex_scalar () || tc.is_complex_matrix ())
02199     {
02200       if (tc.is_single_type ())
02201         {
02202           const FloatComplexNDArray m = tc.float_complex_array_value ();
02203           ret += save_mat5_array_length (m.fortran_vec (), m.numel (),
02204                                          save_as_floats);
02205         }
02206       else
02207         {
02208           const ComplexNDArray m = tc.complex_array_value ();
02209           ret += save_mat5_array_length (m.fortran_vec (), m.numel (),
02210                                          save_as_floats);
02211         }
02212     }
02213   else if (tc.is_map () || tc.is_inline_function () || tc.is_object ())
02214     {
02215       int fieldcnt = 0;
02216       const Octave_map m = tc.map_value ();
02217       octave_idx_type nel = m.numel ();
02218 
02219       if (tc.is_inline_function ())
02220         // length of "inline" is 6
02221         ret += 8 + PAD (6 > max_namelen ? max_namelen : 6);
02222       else if (tc.is_object ())
02223         {
02224           size_t classlen = tc.class_name (). length ();
02225 
02226           ret += 8 + PAD (classlen > max_namelen ? max_namelen : classlen);
02227         }
02228 
02229       for (Octave_map::const_iterator i = m.begin (); i != m.end (); i++)
02230         fieldcnt++;
02231 
02232       ret += 16 + fieldcnt * (max_namelen + 1);
02233 
02234 
02235       for (octave_idx_type j = 0; j < nel; j++)
02236         {
02237 
02238           for (Octave_map::const_iterator i = m.begin (); i != m.end (); i++)
02239             {
02240               const Cell elts = m.contents (i);
02241 
02242               ret += 8 + save_mat5_element_length (elts(j), "",
02243                                                save_as_floats, mat7_format);
02244             }
02245         }
02246     }
02247   else
02248     ret = -1;
02249 
02250   return ret;
02251 }
02252 
02253 static void
02254 write_mat5_sparse_index_vector (std::ostream& os,
02255                                 const octave_idx_type *idx,
02256                                 octave_idx_type nel)
02257 {
02258   int tmp = sizeof (int32_t);
02259 
02260   OCTAVE_LOCAL_BUFFER (int32_t, tmp_idx, nel);
02261 
02262   for (octave_idx_type i = 0; i < nel; i++)
02263     tmp_idx[i] = idx[i];
02264 
02265   write_mat5_integer_data (os, tmp_idx, -tmp, nel);
02266 }
02267 
02268 static void
02269 gripe_dim_too_large (const std::string& name)
02270 {
02271   warning ("save: skipping %s: dimension too large for MAT format",
02272            name.c_str ());
02273 }
02274 
02275 // save the data from TC along with the corresponding NAME on stream
02276 // OS in the MatLab version 5 binary format.  Return true on success.
02277 
02278 bool
02279 save_mat5_binary_element (std::ostream& os,
02280                           const octave_value& tc, const std::string& name,
02281                           bool mark_as_global, bool mat7_format,
02282                           bool save_as_floats, bool compressing)
02283 {
02284   int32_t flags = 0;
02285   int32_t nnz_32 = 0;
02286   std::string cname = tc.class_name ();
02287   size_t max_namelen = (mat7_format ? 63 : 31);
02288 
02289   dim_vector dv = tc.dims ();
02290   int nd = tc.ndims ();
02291   int dim_len = 4*nd;
02292 
02293   static octave_idx_type max_dim_val = std::numeric_limits<int32_t>::max ();
02294 
02295   for (int i = 0; i < nd; i++)
02296     {
02297       if (dv(i) > max_dim_val)
02298         {
02299           gripe_dim_too_large (name);
02300           goto skip_to_next;
02301         }
02302     }
02303 
02304   if (tc.is_sparse_type ())
02305     {
02306       octave_idx_type nnz;
02307       octave_idx_type nc;
02308 
02309       if (tc.is_complex_type ())
02310         {
02311           SparseComplexMatrix scm = tc.sparse_complex_matrix_value ();
02312           nnz = scm.nzmax ();
02313           nc = scm.cols ();
02314         }
02315       else
02316         {
02317           SparseMatrix sm = tc.sparse_matrix_value ();
02318           nnz = sm.nzmax ();
02319           nc = sm.cols ();
02320         }
02321 
02322       if (nnz > max_dim_val || nc + 1 > max_dim_val)
02323         {
02324           gripe_dim_too_large (name);
02325           goto skip_to_next;
02326         }
02327 
02328       nnz_32 = nnz;
02329     }
02330   else if (dv.numel () > max_dim_val)
02331     {
02332       gripe_dim_too_large (name);
02333       goto skip_to_next;
02334     }
02335 
02336 #ifdef HAVE_ZLIB
02337   if (mat7_format && !compressing)
02338     {
02339       bool ret = false;
02340 
02341       std::ostringstream buf;
02342 
02343       // The code seeks backwards in the stream to fix the header. Can't
02344       // do this with zlib, so use a stringstream.
02345       ret = save_mat5_binary_element (buf, tc, name, mark_as_global, true,
02346                                       save_as_floats, true);
02347 
02348       if (ret)
02349         {
02350           // destLen must be at least 0.1% larger than source buffer
02351           // + 12 bytes. Reality is it must be larger again than that.
02352           std::string buf_str = buf.str ();
02353           uLongf srcLen = buf_str.length ();
02354           uLongf destLen = srcLen * 101 / 100 + 12;
02355           OCTAVE_LOCAL_BUFFER (char, out_buf, destLen);
02356 
02357           if (compress (reinterpret_cast<Bytef *> (out_buf), &destLen,
02358                         reinterpret_cast<const Bytef *> (buf_str.c_str ()), srcLen) == Z_OK)
02359             {
02360               write_mat5_tag (os, miCOMPRESSED,
02361                               static_cast<octave_idx_type> (destLen));
02362 
02363               os.write (out_buf, destLen);
02364             }
02365           else
02366             {
02367               error ("save: error compressing data element");
02368               ret = false;
02369             }
02370         }
02371 
02372       return ret;
02373     }
02374 #endif
02375 
02376   write_mat5_tag (os, miMATRIX, save_mat5_element_length
02377                   (tc, name, save_as_floats, mat7_format));
02378 
02379   // array flags subelement
02380   write_mat5_tag (os, miUINT32, 8);
02381 
02382   if (tc.is_bool_type ())
02383     flags |= 0x0200;
02384 
02385   if (mark_as_global)
02386     flags |= 0x0400;
02387 
02388   if (tc.is_complex_scalar () || tc.is_complex_matrix ())
02389     flags |= 0x0800;
02390 
02391   if (tc.is_string ())
02392     flags |= MAT_FILE_CHAR_CLASS;
02393   else if (cname == "int8")
02394     flags |= MAT_FILE_INT8_CLASS;
02395   else if (cname == "int16")
02396     flags |= MAT_FILE_INT16_CLASS;
02397   else if (cname == "int32")
02398     flags |= MAT_FILE_INT32_CLASS;
02399   else if (cname == "int64")
02400     flags |= MAT_FILE_INT64_CLASS;
02401   else if (cname == "uint8" || tc.is_bool_type ())
02402     flags |= MAT_FILE_UINT8_CLASS;
02403   else if (cname == "uint16")
02404     flags |= MAT_FILE_UINT16_CLASS;
02405   else if (cname == "uint32")
02406     flags |= MAT_FILE_UINT32_CLASS;
02407   else if (cname == "uint64")
02408     flags |= MAT_FILE_UINT64_CLASS;
02409   else if (tc.is_sparse_type ())
02410     flags |= MAT_FILE_SPARSE_CLASS;
02411   else if (tc.is_real_scalar () || tc.is_real_matrix () || tc.is_range ()
02412            || tc.is_complex_scalar () || tc.is_complex_matrix ())
02413     {
02414       if (tc.is_single_type ())
02415         flags |= MAT_FILE_SINGLE_CLASS;
02416       else
02417         flags |= MAT_FILE_DOUBLE_CLASS;
02418     }
02419   else if (tc.is_map ())
02420     flags |= MAT_FILE_STRUCT_CLASS;
02421   else if (tc.is_cell ())
02422     flags |= MAT_FILE_CELL_CLASS;
02423   else if (tc.is_inline_function () || tc.is_object ())
02424     flags |= MAT_FILE_OBJECT_CLASS;
02425   else
02426     {
02427       gripe_wrong_type_arg ("save", tc, false);
02428       goto error_cleanup;
02429     }
02430 
02431   os.write (reinterpret_cast<char *> (&flags), 4);
02432   os.write (reinterpret_cast<char *> (&nnz_32), 4);
02433 
02434   write_mat5_tag (os, miINT32, dim_len);
02435 
02436   for (int i = 0; i < nd; i++)
02437     {
02438       int32_t n = dv(i);
02439       os.write (reinterpret_cast<char *> (&n), 4);
02440     }
02441 
02442   if (PAD (dim_len) > dim_len)
02443     {
02444       static char buf[9]="\x00\x00\x00\x00\x00\x00\x00\x00";
02445       os.write (buf, PAD (dim_len) - dim_len);
02446     }
02447 
02448   // array name subelement
02449   {
02450     size_t namelen = name.length ();
02451 
02452     if (namelen > max_namelen)
02453       namelen = max_namelen; // only 31 or 63 char names permitted in mat file
02454 
02455     int paddedlength = PAD (namelen);
02456 
02457     write_mat5_tag (os, miINT8, namelen);
02458     OCTAVE_LOCAL_BUFFER (char, paddedname, paddedlength);
02459     memset (paddedname, 0, paddedlength);
02460     strncpy (paddedname, name.c_str (), namelen);
02461     os.write (paddedname, paddedlength);
02462   }
02463 
02464   // data element
02465   if (tc.is_string ())
02466     {
02467       charNDArray chm = tc.char_array_value ();
02468       octave_idx_type nel = chm.numel ();
02469       octave_idx_type len = nel*2;
02470       octave_idx_type paddedlength = PAD (len);
02471 
02472       OCTAVE_LOCAL_BUFFER (int16_t, buf, nel+3);
02473       write_mat5_tag (os, miUINT16, len);
02474 
02475       const char *s = chm.data ();
02476 
02477       for (octave_idx_type i = 0; i < nel; i++)
02478         buf[i] = *s++ & 0x00FF;
02479 
02480       os.write (reinterpret_cast<char *> (buf), len);
02481 
02482       if (paddedlength > len)
02483         {
02484           static char padbuf[9]="\x00\x00\x00\x00\x00\x00\x00\x00";
02485           os.write (padbuf, paddedlength - len);
02486         }
02487     }
02488   else if (tc.is_sparse_type ())
02489     {
02490       if (tc.is_complex_type ())
02491         {
02492           const SparseComplexMatrix m = tc.sparse_complex_matrix_value ();
02493           octave_idx_type nnz = m.nnz ();
02494           octave_idx_type nc = m.cols ();
02495 
02496           write_mat5_sparse_index_vector (os, m.ridx (), nnz);
02497           write_mat5_sparse_index_vector (os, m.cidx (), nc + 1);
02498 
02499           NDArray buf (dim_vector (nnz, 1));
02500 
02501           for (octave_idx_type i = 0; i < nnz; i++)
02502             buf (i) = std::real (m.data (i));
02503 
02504           write_mat5_array (os, buf, save_as_floats);
02505 
02506           for (octave_idx_type i = 0; i < nnz; i++)
02507             buf (i) = std::imag (m.data (i));
02508 
02509           write_mat5_array (os, buf, save_as_floats);
02510         }
02511       else
02512         {
02513           const SparseMatrix m = tc.sparse_matrix_value ();
02514           octave_idx_type nnz = m.nnz ();
02515           octave_idx_type nc = m.cols ();
02516 
02517           write_mat5_sparse_index_vector (os, m.ridx (), nnz);
02518           write_mat5_sparse_index_vector (os, m.cidx (), nc + 1);
02519 
02520           // FIXME
02521           // Is there a way to easily do without this buffer
02522           NDArray buf (dim_vector (nnz, 1));
02523 
02524           for (int i = 0; i < nnz; i++)
02525             buf (i) = m.data (i);
02526 
02527           write_mat5_array (os, buf, save_as_floats);
02528         }
02529     }
02530   else if (cname == "int8")
02531     {
02532       int8NDArray m = tc.int8_array_value ();
02533 
02534       write_mat5_integer_data (os, m.fortran_vec (), -1, m.numel ());
02535     }
02536   else if (cname == "int16")
02537     {
02538       int16NDArray m = tc.int16_array_value ();
02539 
02540       write_mat5_integer_data (os, m.fortran_vec (), -2, m.numel ());
02541     }
02542   else if (cname == "int32")
02543     {
02544       int32NDArray m = tc.int32_array_value ();
02545 
02546       write_mat5_integer_data (os, m.fortran_vec (), -4, m.numel ());
02547     }
02548   else if (cname == "int64")
02549     {
02550       int64NDArray m = tc.int64_array_value ();
02551 
02552       write_mat5_integer_data (os, m.fortran_vec (), -8, m.numel ());
02553     }
02554   else if (cname == "uint8")
02555     {
02556       uint8NDArray m = tc.uint8_array_value ();
02557 
02558       write_mat5_integer_data (os, m.fortran_vec (), 1, m.numel ());
02559     }
02560   else if (cname == "uint16")
02561     {
02562       uint16NDArray m = tc.uint16_array_value ();
02563 
02564       write_mat5_integer_data (os, m.fortran_vec (), 2, m.numel ());
02565     }
02566   else if (cname == "uint32")
02567     {
02568       uint32NDArray m = tc.uint32_array_value ();
02569 
02570       write_mat5_integer_data (os, m.fortran_vec (), 4, m.numel ());
02571     }
02572   else if (cname == "uint64")
02573     {
02574       uint64NDArray m = tc.uint64_array_value ();
02575 
02576       write_mat5_integer_data (os, m.fortran_vec (), 8, m.numel ());
02577     }
02578   else if (tc.is_bool_type ())
02579     {
02580       uint8NDArray m (tc.bool_array_value ());
02581 
02582       write_mat5_integer_data (os, m.fortran_vec (), 1, m.numel ());
02583     }
02584   else if (tc.is_real_scalar () || tc.is_real_matrix () || tc.is_range ())
02585     {
02586       if (tc.is_single_type ())
02587         {
02588           FloatNDArray m = tc.float_array_value ();
02589 
02590           write_mat5_array (os, m, save_as_floats);
02591         }
02592       else
02593         {
02594           NDArray m = tc.array_value ();
02595 
02596           write_mat5_array (os, m, save_as_floats);
02597         }
02598     }
02599   else if (tc.is_cell ())
02600     {
02601       Cell cell = tc.cell_value ();
02602 
02603       if (! write_mat5_cell_array (os, cell, mark_as_global, save_as_floats))
02604         goto error_cleanup;
02605     }
02606   else if (tc.is_complex_scalar () || tc.is_complex_matrix ())
02607     {
02608       if (tc.is_single_type ())
02609         {
02610           FloatComplexNDArray m_cmplx = tc.float_complex_array_value ();
02611 
02612           write_mat5_array (os, ::real (m_cmplx), save_as_floats);
02613           write_mat5_array (os, ::imag (m_cmplx), save_as_floats);
02614         }
02615       else
02616         {
02617           ComplexNDArray m_cmplx = tc.complex_array_value ();
02618 
02619           write_mat5_array (os, ::real (m_cmplx), save_as_floats);
02620           write_mat5_array (os, ::imag (m_cmplx), save_as_floats);
02621         }
02622     }
02623   else if (tc.is_map () || tc.is_inline_function() || tc.is_object ())
02624     {
02625       if (tc.is_inline_function () || tc.is_object ())
02626         {
02627           std::string classname = tc.is_object() ? tc.class_name () : "inline";
02628           size_t namelen = classname.length ();
02629 
02630           if (namelen > max_namelen)
02631             namelen = max_namelen; // only 31 or 63 char names permitted
02632 
02633           int paddedlength = PAD (namelen);
02634 
02635           write_mat5_tag (os, miINT8, namelen);
02636           OCTAVE_LOCAL_BUFFER (char, paddedname, paddedlength);
02637           memset (paddedname, 0, paddedlength);
02638           strncpy (paddedname, classname.c_str (), namelen);
02639           os.write (paddedname, paddedlength);
02640         }
02641 
02642       Octave_map m;
02643 
02644       if (tc.is_object () &&
02645           load_path::find_method (tc.class_name (), "saveobj") != std::string())
02646         {
02647           octave_value_list tmp = feval ("saveobj", tc, 1);
02648           if (! error_state)
02649             m = tmp(0).map_value ();
02650           else
02651             goto error_cleanup;
02652         }
02653       else
02654         m = tc.map_value ();
02655 
02656       // an Octave structure */
02657       // recursively write each element of the structure
02658       {
02659         char buf[64];
02660         int32_t maxfieldnamelength = max_namelen + 1;
02661 
02662         octave_idx_type nf = m.nfields ();
02663 
02664         write_mat5_tag (os, miINT32, 4);
02665         os.write (reinterpret_cast<char *> (&maxfieldnamelength), 4);
02666         write_mat5_tag (os, miINT8, nf*maxfieldnamelength);
02667 
02668         // Iterating over the list of keys will preserve the order of
02669         // the fields.
02670         string_vector keys = m.keys ();
02671 
02672         for (octave_idx_type i = 0; i < nf; i++)
02673           {
02674             std::string key = keys(i);
02675 
02676             // write the name of each element
02677             memset (buf, 0, max_namelen + 1);
02678             // only 31 or 63 char names permitted
02679             strncpy (buf, key.c_str (), max_namelen);
02680             os.write (buf, max_namelen + 1);
02681           }
02682 
02683         octave_idx_type len = m.numel ();
02684 
02685         // Create temporary copy of structure contents to avoid
02686         // multiple calls of the contents method.
02687         std::vector<const octave_value *> elts (nf);
02688         for (octave_idx_type i = 0; i < nf; i++)
02689           elts[i] = m.contents (keys(i)).data ();
02690 
02691         for (octave_idx_type j = 0; j < len; j++)
02692           {
02693             // write the data of each element
02694 
02695             // Iterating over the list of keys will preserve the order
02696             // of the fields.
02697             for (octave_idx_type i = 0; i < nf; i++)
02698               {
02699                 bool retval2 = save_mat5_binary_element (os, elts[i][j], "",
02700                                                          mark_as_global,
02701                                                          false,
02702                                                          save_as_floats);
02703                 if (! retval2)
02704                   goto error_cleanup;
02705               }
02706           }
02707       }
02708     }
02709   else
02710     gripe_wrong_type_arg ("save", tc, false);
02711 
02712  skip_to_next:
02713   return true;
02714 
02715  error_cleanup:
02716   error ("save: error while writing '%s' to MAT file", name.c_str ());
02717 
02718   return false;
02719 }
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines