Logo Search packages:      
Sourcecode: octave3.0 version File versions

ls-mat5.cc

/*

Copyright (C) 1996, 1997, 2003, 2004, 2005, 2006, 2007 John W. Eaton

This file is part of Octave.

Octave is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version.

Octave is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with Octave; see the file COPYING.  If not, see
<http://www.gnu.org/licenses/>.

*/

// Author: James R. Van Zandt <jrv@vanzandt.mv.com>

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <cfloat>
#include <cstring>
#include <cctype>

#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

#include "byte-swap.h"
#include "data-conv.h"
#include "file-ops.h"
#include "glob-match.h"
#include "lo-mappers.h"
#include "mach-info.h"
#include "oct-env.h"
#include "oct-time.h"
#include "quit.h"
#include "str-vec.h"
#include "file-stat.h"

#include "Cell.h"
#include "defun.h"
#include "error.h"
#include "gripes.h"
#include "load-save.h"
#include "load-path.h"
#include "oct-obj.h"
#include "oct-map.h"
#include "ov-cell.h"
#include "ov-fcn-inline.h"
#include "pager.h"
#include "pt-exp.h"
#include "symtab.h"
#include "sysdep.h"
#include "unwind-prot.h"
#include "utils.h"
#include "variables.h"
#include "version.h"
#include "dMatrix.h"

#include "ls-utils.h"
#include "ls-mat5.h"

#include "parse.h"
#include "defaults.h"

#ifdef HAVE_ZLIB
#include <zlib.h>
#endif

#define PAD(l) (((l) > 0 && (l) <= 4) ? 4 : (((l)+7)/8)*8)


// The subsystem data block
static octave_value subsys_ov;

// FIXME -- the following enum values should be the same as the
// mxClassID values in mexproto.h, but it seems they have also changed
// over time.  What is the correct way to handle this and maintain
// backward compatibility with old MAT files?  For now, use
// "MAT_FILE_" instead of "mx" as the prefix for these names to avoid
// conflict with the mxClassID enum in mexproto.h.

enum arrayclasstype
  {
    MAT_FILE_CELL_CLASS=1,          // cell array
    MAT_FILE_STRUCT_CLASS,          // structure
    MAT_FILE_OBJECT_CLASS,          // object
    MAT_FILE_CHAR_CLASS,            // character array
    MAT_FILE_SPARSE_CLASS,          // sparse array
    MAT_FILE_DOUBLE_CLASS,          // double precision array
    MAT_FILE_SINGLE_CLASS,          // single precision floating point
    MAT_FILE_INT8_CLASS,            // 8 bit signed integer
    MAT_FILE_UINT8_CLASS,           // 8 bit unsigned integer
    MAT_FILE_INT16_CLASS,           // 16 bit signed integer
    MAT_FILE_UINT16_CLASS,          // 16 bit unsigned integer
    MAT_FILE_INT32_CLASS,           // 32 bit signed integer
    MAT_FILE_UINT32_CLASS,          // 32 bit unsigned integer
    MAT_FILE_INT64_CLASS,           // 64 bit signed integer
    MAT_FILE_UINT64_CLASS,          // 64 bit unsigned integer
    MAT_FILE_FUNCTION_CLASS,            // Function handle
    MAT_FILE_WORKSPACE_CLASS        // Workspace (undocumented)
  };

// Read COUNT elements of data from IS in the format specified by TYPE,
// placing the result in DATA.  If SWAP is TRUE, swap the bytes of
// each element before copying to DATA.  FLT_FMT specifies the format
// of the data if we are reading floating point numbers.

static void
read_mat5_binary_data (std::istream& is, double *data,
                   int count, bool swap, mat5_data_type type,
                   oct_mach_info::float_format flt_fmt)
{
  
  switch (type)
    {
    case miINT8:
      read_doubles (is, data, LS_CHAR, count, swap, flt_fmt);
      break;

    case miUTF8:
    case miUINT8:
      read_doubles (is, data, LS_U_CHAR, count, swap, flt_fmt);
      break;

    case miINT16:
      read_doubles (is, data, LS_SHORT, count, swap, flt_fmt);
      break;

    case miUTF16:
    case miUINT16:
      read_doubles (is, data, LS_U_SHORT, count, swap, flt_fmt);
      break;

    case miINT32:
      read_doubles (is, data, LS_INT, count, swap, flt_fmt);
      break;

    case miUTF32:
    case miUINT32:
      read_doubles (is, data, LS_U_INT, count, swap, flt_fmt);
      break;

    case miSINGLE:
      read_doubles (is, data, LS_FLOAT, count, swap, flt_fmt);
      break;

    case miRESERVE1:
      break;

    case miDOUBLE:
      read_doubles (is, data, LS_DOUBLE, count, swap, flt_fmt);
      break;

    case miRESERVE2:
    case miRESERVE3:
      break;

    // FIXME -- how are the 64-bit cases supposed to work here?
    case miINT64:
      read_doubles (is, data, LS_LONG, count, swap, flt_fmt);
      break;

    case miUINT64:
      read_doubles (is, data, LS_U_LONG, count, swap, flt_fmt);
      break;

    case miMATRIX:
    default:
      break;
    }
}

template <class T>
void
read_mat5_integer_data (std::istream& is, T *m, int count, bool swap,
                  mat5_data_type type)
{

#define READ_INTEGER_DATA(TYPE, swap, data, size, len, stream)    \
  do \
    { \
      if (len > 0) \
      { \
        OCTAVE_LOCAL_BUFFER (TYPE, ptr, len); \
        stream.read (reinterpret_cast<char *> (ptr), size * len); \
        if (swap) \
          swap_bytes< size > (ptr, len); \
        for (int i = 0; i < len; i++) \
          data[i] = ptr[i]; \
      } \
    } \
  while (0)

  switch (type)
    {
    case miINT8:
      READ_INTEGER_DATA (int8_t, swap, m, 1, count, is);
      break;

    case miUINT8:
      READ_INTEGER_DATA (uint8_t, swap, m, 1, count, is);
      break;

    case miINT16:
      READ_INTEGER_DATA (int16_t, swap, m, 2, count, is);
      break;

    case miUINT16:
      READ_INTEGER_DATA (uint16_t, swap, m, 2, count, is);
      break;

    case miINT32:
      READ_INTEGER_DATA (int32_t, swap, m, 4, count, is);
      break;

    case miUINT32:
      READ_INTEGER_DATA (uint32_t, swap, m, 4, count, is);
      break;

    case miSINGLE:
    case miRESERVE1:
    case miDOUBLE:
    case miRESERVE2:
    case miRESERVE3:
      break;

    case miINT64:
      READ_INTEGER_DATA (int64_t, swap, m, 8, count, is);
      break;

    case miUINT64:
      READ_INTEGER_DATA (uint64_t, swap, m, 8, count, is);
      break;

    case miMATRIX:
    default:
      break;
    }

#undef READ_INTEGER_DATA

}

template void read_mat5_integer_data (std::istream& is, octave_int8 *m,
                              int count, bool swap,
                              mat5_data_type type);
template void read_mat5_integer_data (std::istream& is, octave_int16 *m,
                              int count, bool swap,
                              mat5_data_type type);
template void read_mat5_integer_data (std::istream& is, octave_int32 *m,
                              int count, bool swap,
                              mat5_data_type type);
template void read_mat5_integer_data (std::istream& is, octave_int64 *m,
                              int count, bool swap,
                              mat5_data_type type);
template void read_mat5_integer_data (std::istream& is, octave_uint8 *m,
                              int count, bool swap,
                              mat5_data_type type);
template void read_mat5_integer_data (std::istream& is, octave_uint16 *m,
                              int count, bool swap,
                              mat5_data_type type);
template void read_mat5_integer_data (std::istream& is, octave_uint32 *m,
                              int count, bool swap,
                              mat5_data_type type);
template void read_mat5_integer_data (std::istream& is, octave_uint64 *m,
                              int count, bool swap,
                              mat5_data_type type);

template void read_mat5_integer_data (std::istream& is, int *m,
                              int count, bool swap,
                              mat5_data_type type);

#define OCTAVE_MAT5_INTEGER_READ(TYP) \
  { \
      TYP re (dims); \
  \
      std::streampos tmp_pos; \
  \
      if (read_mat5_tag (is, swap, type, len)) \
        { \
          error ("load: reading matrix data for `%s'", retval.c_str ()); \
          goto data_read_error; \
        } \
  \
      int n = re.length (); \
      tmp_pos = is.tellg (); \
      read_mat5_integer_data (is, re.fortran_vec (), n, swap,     \
                        static_cast<enum mat5_data_type> (type)); \
  \
      if (! is || error_state) \
        { \
          error ("load: reading matrix data for `%s'", retval.c_str ()); \
          goto data_read_error; \
        } \
  \
      is.seekg (tmp_pos + static_cast<std::streamoff> (PAD (len))); \
  \
      if (imag) \
        { \
          /* We don't handle imag integer types, convert to an array */ \
          NDArray im (dims); \
  \
          if (read_mat5_tag (is, swap, type, len)) \
            { \
            error ("load: reading matrix data for `%s'", \
                   retval.c_str ()); \
            goto data_read_error; \
            } \
  \
          n = im.length (); \
          read_mat5_binary_data (is, im.fortran_vec (), n, swap, \
                           static_cast<enum mat5_data_type> (type), flt_fmt); \
  \
          if (! is || error_state) \
            { \
            error ("load: reading imaginary matrix data for `%s'", \
                   retval.c_str ()); \
            goto data_read_error; \
            } \
  \
          ComplexNDArray ctmp (dims); \
  \
          for (int i = 0; i < n; i++) \
            ctmp(i) = Complex (re(i).double_value (), im(i)); \
  \
            tc = ctmp;  \
        } \
      else \
        tc = re; \
  }
  
// Read one element tag from stream IS, 
// place the type code in TYPE and the byte count in BYTES
// return nonzero on error
static int
read_mat5_tag (std::istream& is, bool swap, int32_t& type, int32_t& bytes)
{
  unsigned int upper;
  int32_t temp;

  if (! is.read (reinterpret_cast<char *> (&temp), 4 ))
    goto data_read_error;

  if (swap)
    swap_bytes<4> (&temp);

  upper = (temp >> 16) & 0xffff;
  type = temp & 0xffff;

  if (upper)
    {
      // "compressed" format
      bytes = upper;
    }
  else
    {
      if (! is.read (reinterpret_cast<char *> (&temp), 4 ))
      goto data_read_error;
      if (swap)
      swap_bytes<4> (&temp);
      bytes = temp;
    }

  return 0;

 data_read_error:
  return 1;
}

static void
read_int (std::istream& is, bool swap, int32_t& val)
{
  is.read (reinterpret_cast<char *> (&val), 4);

  if (swap)
    swap_bytes<4> (&val);
}

// Extract one data element (scalar, matrix, string, etc.) from stream
// IS and place it in TC, returning the name of the variable.
//
// The data is expected to be in Matlab's "Version 5" .mat format,
// though not all the features of that format are supported.
//
// FILENAME is used for error messages.

std::string
read_mat5_binary_element (std::istream& is, const std::string& filename,
                    bool swap, bool& global, octave_value& tc)
{
  std::string retval;

  // These are initialized here instead of closer to where they are
  // first used to avoid errors from gcc about goto crossing
  // initialization of variable.

  oct_mach_info::float_format flt_fmt = oct_mach_info::flt_fmt_unknown;
  int32_t type = 0;
  std::string classname;
  bool isclass = false;
  bool imag;
  bool logicalvar;
  enum arrayclasstype arrayclass;
  int32_t nzmax;
  int32_t flags;
  dim_vector dims;
  int32_t len;
  int32_t element_length;
  std::streampos pos;
  int16_t number;
  number = *(reinterpret_cast<const int16_t *>("\x00\x01"));

  global = false;

  // MAT files always use IEEE floating point
  if ((number == 1) ^ swap)
    flt_fmt = oct_mach_info::flt_fmt_ieee_big_endian;
  else
    flt_fmt = oct_mach_info::flt_fmt_ieee_little_endian;

  // element type and length
  if (read_mat5_tag (is, swap, type, element_length))
    return retval;                  // EOF

#ifdef HAVE_ZLIB
  if (type == miCOMPRESSED)
    {
      // If C++ allowed us direct access to the file descriptor of an ifstream 
      // in a uniform way, the code below could be vastly simplified, and 
      // additional copies of the data in memory wouldn't be needed!!

      OCTAVE_LOCAL_BUFFER (char, inbuf, element_length);
      is.read (inbuf, element_length);

      // We uncompress the first 8 bytes of the header to get the buffer length
      // This will fail with an error Z_MEM_ERROR
      uLongf destLen = 8;
      OCTAVE_LOCAL_BUFFER (unsigned int, tmp, 2);
      if (uncompress (reinterpret_cast<Bytef *> (tmp), &destLen, 
                  reinterpret_cast<Bytef *> (inbuf), element_length)
        !=  Z_MEM_ERROR)
      {
        // Why should I have to initialize outbuf as I'll just overwrite!!
        if (swap)
          swap_bytes<4> (tmp, 2);

        destLen = tmp[1] + 8;
        std::string outbuf (destLen, ' '); 

        // FIXME -- find a way to avoid casting away const here!

        int err = uncompress (reinterpret_cast<Bytef *> (const_cast<char *> (outbuf.c_str ())), &destLen, 
                        reinterpret_cast<Bytef *> (inbuf), element_length);

        if (err != Z_OK)
          error ("load: error uncompressing data element");
        else
          {
            std::istringstream gz_is (outbuf);
            retval = read_mat5_binary_element (gz_is, filename, 
                                     swap, global, tc);
          }
      }
      else
      error ("load: error probing size of compressed data element");

      return retval;
    }
#endif

  if (type != miMATRIX)
    {
      pos = is.tellg ();
      error ("load: invalid element type = %d", type);
      goto early_read_error;
    }

  if (element_length == 0)
    {
      tc = Matrix ();
      return retval;
    }

  pos = is.tellg ();

  // array flags subelement
  if (read_mat5_tag (is, swap, type, len) || type != miUINT32 || len != 8)
    {
      error ("load: invalid array flags subelement");
      goto early_read_error;
    }

  read_int (is, swap, flags);
  imag = (flags & 0x0800) != 0;     // has an imaginary part?
  global = (flags & 0x0400) != 0; // global variable?
  logicalvar = (flags & 0x0200) != 0; // boolean ?
  arrayclass = static_cast<arrayclasstype> (flags & 0xff);
  read_int (is, swap, nzmax); // max number of non-zero in sparse
  
  // dimensions array subelement
  if (arrayclass != MAT_FILE_WORKSPACE_CLASS)
    {
      int32_t dim_len;

      if (read_mat5_tag (is, swap, type, dim_len) || type != miINT32)
      {
        error ("load: invalid dimensions array subelement");
        goto early_read_error;
      }

      int ndims = dim_len / 4;
      dims.resize (ndims);
      for (int i = 0; i < ndims; i++)
      {
        int32_t n;
        read_int (is, swap, n);
        dims(i) = n;
      }

      std::streampos tmp_pos = is.tellg ();
      is.seekg (tmp_pos + static_cast<std::streamoff> (PAD (dim_len) - dim_len));
    }
  else
    {
      // Why did mathworks decide to not have dims for a workspace!!!
      dims.resize(2);
      dims(0) = 1;
      dims(1) = 1;
    }

  if (read_mat5_tag (is, swap, type, len) || type != miINT8)
    {
      error ("load: invalid array name subelement");
      goto early_read_error;
    }

  {
    OCTAVE_LOCAL_BUFFER (char, name, len+1);

    // Structure field subelements have zero-length array name subelements.

    std::streampos tmp_pos = is.tellg ();

    if (len)
      {
      if (! is.read (name, len ))
        goto data_read_error;
      
      is.seekg (tmp_pos + static_cast<std::streamoff> (PAD (len)));
      }

    name[len] = '\0';
    retval = name;
  }

  switch (arrayclass)
    {
    case MAT_FILE_CELL_CLASS:
      {
      Cell cell_array (dims);

      int n = cell_array.length ();

      for (int i = 0; i < n; i++)
        {
          octave_value tc2;

          std::string nm
            = read_mat5_binary_element (is, filename, swap, global, tc2);

          if (! is || error_state)
            {
            error ("load: reading cell data for `%s'", nm.c_str ());
            goto data_read_error;
            }

          cell_array(i) = tc2;
        }

      tc = cell_array;
      }
      break;

    case MAT_FILE_SPARSE_CLASS:
#if SIZEOF_INT != SIZEOF_OCTAVE_IDX_TYPE
      warning ("load: sparse objects are not implemented");
      goto skip_ahead;
#else
      {
      int nr = dims(0);
      int nc = dims(1);
      SparseMatrix sm;
      SparseComplexMatrix scm;
      int *ridx;
      int *cidx;
      double *data;

      // Setup return value
      if (imag)
        {
          scm = SparseComplexMatrix (static_cast<octave_idx_type> (nr),
                               static_cast<octave_idx_type> (nc),
                               static_cast<octave_idx_type> (nzmax));
          ridx = scm.ridx ();
          cidx = scm.cidx ();
          data = 0;
        }
      else
        {
          sm = SparseMatrix (static_cast<octave_idx_type> (nr),
                         static_cast<octave_idx_type> (nc),
                         static_cast<octave_idx_type> (nzmax));
          ridx = sm.ridx ();
          cidx = sm.cidx ();
          data = sm.data ();
        }

      // row indices
      std::streampos tmp_pos;
        
      if (read_mat5_tag (is, swap, type, len))
        {
          error ("load: reading sparse row data for `%s'", retval.c_str ());
          goto data_read_error;
        }

      tmp_pos = is.tellg ();

      read_mat5_integer_data (is, ridx, nzmax, swap,
                        static_cast<enum mat5_data_type> (type));

      if (! is || error_state)
        {
          error ("load: reading sparse row data for `%s'", retval.c_str ());
          goto data_read_error;
        }

      is.seekg (tmp_pos + static_cast<std::streamoff> (PAD (len)));

      // col indices
      if (read_mat5_tag (is, swap, type, len))
        {
          error ("load: reading sparse column data for `%s'", retval.c_str ());
          goto data_read_error;
        }

      tmp_pos = is.tellg ();

      read_mat5_integer_data (is, cidx, nc + 1, swap,
                        static_cast<enum mat5_data_type> (type));

      if (! is || error_state)
        {
          error ("load: reading sparse column data for `%s'", retval.c_str ());
          goto data_read_error;
        }

      is.seekg (tmp_pos + static_cast<std::streamoff> (PAD (len)));

      // real data subelement
      if (read_mat5_tag (is, swap, type, len))
        {
          error ("load: reading sparse matrix data for `%s'", retval.c_str ());
          goto data_read_error;
        }

      int32_t nnz = cidx[nc];
      NDArray re;
      if (imag)
        {
          re = NDArray (dim_vector (static_cast<int> (nnz)));
          data = re.fortran_vec ();
        }

      tmp_pos = is.tellg ();
      read_mat5_binary_data (is, data, nnz, swap,
                         static_cast<enum mat5_data_type> (type), flt_fmt);

      if (! is || error_state)
        {
          error ("load: reading sparse matrix data for `%s'", retval.c_str ());
          goto data_read_error;
        }

      is.seekg (tmp_pos + static_cast<std::streamoff> (PAD (len)));

      // imaginary data subelement
      if (imag)
        {
          NDArray im (dim_vector (static_cast<int> (nnz)));
        
          if (read_mat5_tag (is, swap, type, len))
            {
            error ("load: reading sparse matrix data for `%s'", retval.c_str ());
            goto data_read_error;
            }

          read_mat5_binary_data (is, im.fortran_vec (), nnz, swap,
                           static_cast<enum mat5_data_type> (type), flt_fmt);

          if (! is || error_state)
            {
            error ("load: reading imaginary sparse matrix data for `%s'",
                   retval.c_str ());
            goto data_read_error;
            }

          for (int i = 0; i < nnz; i++)
            scm.xdata (i) = Complex (re (i), im (i));

          tc = scm;
        }
      else
        tc = sm;
      }
#endif
      break;

    case MAT_FILE_FUNCTION_CLASS:
      {
      octave_value tc2;
      std::string nm
        = read_mat5_binary_element (is, filename, swap, global, tc2);

      if (! is || error_state)
        goto data_read_error;

      // Octave can handle both "/" and "\" as a directry seperator
      // and so can ignore the seperator field of m0. I think the
      // sentinel field is also save to ignore.
      Octave_map m0 = tc2.map_value();
      Octave_map m1 = m0.contents("function_handle")(0).map_value();
      std::string ftype = m1.contents("type")(0).string_value();
      std::string fname = m1.contents("function")(0).string_value();
      std::string fpath = m1.contents("file")(0).string_value();

      if (ftype == "simple" || ftype == "scopedfunction")
        {
          if (fpath.length() == 0)
            // We have a builtin function
            tc = make_fcn_handle (fname);
          else
            {
            std::string mroot = 
              m0.contents("matlabroot")(0).string_value();

            if ((fpath.length () >= mroot.length ()) &&
                fpath.substr(0, mroot.length()) == mroot &&
                OCTAVE_EXEC_PREFIX != mroot)
              {
                // If fpath starts with matlabroot, and matlabroot
                // doesn't equal octave_config_info ("exec_prefix")
                // then the function points to a version of Octave
                // or Matlab other than the running version. In that
                // case we replace with the same function in the
                // running version of Octave?
                
                // First check if just replacing matlabroot is enough
                std::string str = OCTAVE_EXEC_PREFIX + 
                  fpath.substr (mroot.length ());               
                file_stat fs (str);

                if (fs.exists ())
                  {
                  symbol_record *sr = fbi_sym_tab->lookup (str, true);
                
                  if (sr)
                    {
                      load_fcn_from_file (sr, false);

                      tc = octave_value (new octave_fcn_handle 
                                     (sr->def (), fname));

                      // The next two lines are needed to force the 
                      // definition of the function back to the one 
                      // that is on the user path.
                      sr = fbi_sym_tab->lookup (fname, true);

                      load_fcn_from_file (sr, false);
                    }
                  }
                else
                  {
                  // Next just search for it anywhere in the
                  // system path
                  string_vector names(3);
                  names(0) = fname + ".oct";
                  names(1) = fname + ".mex";
                  names(2) = fname + ".m";

                  dir_path p (load_path::system_path ());

                  str = octave_env::make_absolute 
                    (p.find_first_of (names), octave_env::getcwd ());

                  symbol_record *sr = fbi_sym_tab->lookup (str, true);

                  if (sr)
                    {
                      load_fcn_from_file (sr, false);

                      tc = octave_value (new octave_fcn_handle 
                                     (sr->def (), fname));

                      // The next two lines are needed to force the 
                      // definition of the function back to the one 
                      // that is on the user path.
                      sr = fbi_sym_tab->lookup (fname, true);

                      load_fcn_from_file (sr, false);
                    }
                  else
                    {
                      warning ("load: can't find the file %s", 
                             fpath.c_str());
                      goto skip_ahead;
                    }
                  }
              }
            else
              {
                symbol_record *sr = fbi_sym_tab->lookup (fpath, true);

                if (sr)
                  {
                  load_fcn_from_file (sr, false);

                  tc = octave_value (new octave_fcn_handle (sr->def (), 
                                                  fname));

                  sr = fbi_sym_tab->lookup (fname, true);

                  load_fcn_from_file (sr, false);
                  }
                else
                  {
                  warning ("load: can't find the file %s", 
                         fpath.c_str());
                  goto skip_ahead;
                  }
              }
            }
        }
      else if (ftype == "nested")
        {
          warning ("load: can't load nested function");
          goto skip_ahead;
        }
      else if (ftype == "anonymous")
        {
          Octave_map m2 = m1.contents("workspace")(0).map_value();
          uint32NDArray MCOS = m2.contents("MCOS")(0).uint32_array_value();
          octave_idx_type off = static_cast<octave_idx_type>(MCOS(4).double_value ());
          m2 = subsys_ov.map_value();
          m2 = m2.contents("MCOS")(0).map_value();
          tc2 = m2.contents("MCOS")(0).cell_value()(1 + off).cell_value()(1);
          m2 = tc2.map_value();
          symbol_table *local_sym_tab = 0;
          if (m2.nfields() > 0)
            {
            octave_value tmp;

            local_sym_tab = new symbol_table (((m2.nfields() + 1) & ~1), 
                                      "LOCAL");
            
            for (Octave_map::iterator p0 = m2.begin() ; 
                 p0 != m2.end(); p0++)
              {
                std::string key = m2.key(p0);
                octave_value val = m2.contents(p0)(0);

                symbol_record *sr = local_sym_tab->lookup (key, true);

                if (sr)
                  sr->define (val);
                else
                  {
                  error ("load: failed to load anonymous function handle");
                  goto skip_ahead;
                  }
                  }
            }
          
          unwind_protect::begin_frame ("anon_mat5_load");
          unwind_protect_ptr (curr_sym_tab);

          if (local_sym_tab)
            curr_sym_tab = local_sym_tab;

          int parse_status;
          octave_value anon_fcn_handle = 
            eval_string (fname.substr (4), true, parse_status);

          if (parse_status == 0)
            {
            octave_fcn_handle *fh = 
              anon_fcn_handle.fcn_handle_value ();
            if (fh)
              tc = new octave_fcn_handle (fh->fcn_val(), "@<anonymous>");
            else
              {
                error ("load: failed to load anonymous function handle");
                goto skip_ahead;
              }
            }
          else
            {
            error ("load: failed to load anonymous function handle");
            goto skip_ahead;
            }

          unwind_protect::run_frame ("anon_mat5_load");

          if (local_sym_tab)
            delete local_sym_tab;       
        }
      else
        {
          error ("load: invalid function handle type");
          goto skip_ahead;
        }
      }
      break;

    case MAT_FILE_WORKSPACE_CLASS:
      {
      Octave_map m (dim_vector (1, 1));
      int n_fields = 2;
      string_vector field (n_fields);

      for (int i = 0; i < n_fields; i++)
        {
          int32_t fn_type;
          int32_t fn_len;
          if (read_mat5_tag (is, swap, fn_type, fn_len) || fn_type != miINT8)
            {
            error ("load: invalid field name subelement");
            goto data_read_error;
            }

          OCTAVE_LOCAL_BUFFER (char, elname, fn_len + 1);

          std::streampos tmp_pos = is.tellg ();

          if (fn_len)
            {
            if (! is.read (elname, fn_len))
              goto data_read_error;

            is.seekg (tmp_pos + 
                    static_cast<std::streamoff> (PAD (fn_len)));
            }

          elname[fn_len] = '\0';

          field(i) = elname;
        }

      std::vector<Cell> elt (n_fields);

      for (octave_idx_type i = 0; i < n_fields; i++)
        elt[i] = Cell (dims);

      octave_idx_type n = dims.numel ();

      // fields subelements
      for (octave_idx_type j = 0; j < n; j++)
        {
          for (octave_idx_type i = 0; i < n_fields; i++)
            {
            if (field(i) == "MCOS")
              {
                octave_value fieldtc;
                read_mat5_binary_element (is, filename, swap, global,
                                    fieldtc); 
                if (! is || error_state)
                  goto data_read_error;

                elt[i](j) = fieldtc;
              }
            else
              elt[i](j) = octave_value ();
            }
        }

      for (octave_idx_type i = 0; i < n_fields; i++)
        m.assign (field (i), elt[i]);
      tc = m;
      }
      break;

    case MAT_FILE_OBJECT_CLASS:
      {
      isclass = true;

      if (read_mat5_tag (is, swap, type, len) || type != miINT8)
        {
          error ("load: invalid class name");
          goto skip_ahead;
        }

      {
        OCTAVE_LOCAL_BUFFER (char, name, len+1);

        std::streampos tmp_pos = is.tellg ();

        if (len)
          {
            if (! is.read (name, len ))
            goto data_read_error;
      
            is.seekg (tmp_pos + static_cast<std::streamoff> (PAD (len)));
          }

        name[len] = '\0';
        classname = name;
      }
      }
      // Fall-through
    case MAT_FILE_STRUCT_CLASS:
      {
      Octave_map m (dim_vector (1, 1));
      int32_t fn_type;
      int32_t fn_len;
      int32_t field_name_length;

      // field name length subelement -- actually the maximum length
      // of a field name.  The Matlab docs promise this will always
      // be 32.  We read and use the actual value, on the theory
      // that eventually someone will recognize that's a waste of
      // space.
      if (read_mat5_tag (is, swap, fn_type, fn_len) || fn_type != miINT32)
        {
          error ("load: invalid field name length subelement");
          goto data_read_error;
        }

      if (! is.read (reinterpret_cast<char *> (&field_name_length), fn_len ))
        goto data_read_error;

      if (swap)
        swap_bytes<4> (&field_name_length);

      // field name subelement.  The length of this subelement tells
      // us how many fields there are.
      if (read_mat5_tag (is, swap, fn_type, fn_len) || fn_type != miINT8)
        {
          error ("load: invalid field name subelement");
          goto data_read_error;
        }

      octave_idx_type n_fields = fn_len/field_name_length;

      if (n_fields > 0)
        {
          fn_len = PAD (fn_len);

          OCTAVE_LOCAL_BUFFER (char, elname, fn_len);

          if (! is.read (elname, fn_len))
            goto data_read_error;

          std::vector<Cell> elt (n_fields);

          for (octave_idx_type i = 0; i < n_fields; i++)
            elt[i] = Cell (dims);

          octave_idx_type n = dims.numel ();

          // fields subelements
          for (octave_idx_type j = 0; j < n; j++)
            {
            for (octave_idx_type i = 0; i < n_fields; i++)
              {
                octave_value fieldtc;
                read_mat5_binary_element (is, filename, swap, global,
                                    fieldtc); 
                elt[i](j) = fieldtc;
              }
            }

          for (octave_idx_type i = 0; i < n_fields; i++)
            {
            const char *key = elname + i*field_name_length;

            m.assign (key, elt[i]);
            }
        }

      if (isclass)
        {
          if (classname == "inline")
            {
            // inline is not an object in Octave but rather an
            // overload of a function handle. Special case.
            tc =  
              new octave_fcn_inline (m.contents("expr")(0).string_value(),
                               m.contents("args")(0).string_value());
            }
          else
            {
            warning ("load: objects are not implemented");
            goto skip_ahead;
            }
        }
      else
        tc = m;
      }
      break;

    case MAT_FILE_INT8_CLASS:
      OCTAVE_MAT5_INTEGER_READ (int8NDArray);
      break;

    case MAT_FILE_UINT8_CLASS:
      {
      OCTAVE_MAT5_INTEGER_READ (uint8NDArray);

      // Logical variables can either be MAT_FILE_UINT8_CLASS or
      // MAT_FILE_DOUBLE_CLASS, so check if we have a logical
      // variable and convert it.

      if (logicalvar)
        {
          uint8NDArray in = tc.uint8_array_value ();
          int nel = in.nelem ();
          boolNDArray out (dims);
          
          for (int i = 0; i < nel; i++)
            out (i) = in(i).bool_value ();

          tc = out;
        }
      }
      break;

    case MAT_FILE_INT16_CLASS:
      OCTAVE_MAT5_INTEGER_READ (int16NDArray);
      break;

    case MAT_FILE_UINT16_CLASS:
      OCTAVE_MAT5_INTEGER_READ (uint16NDArray);
      break;

    case MAT_FILE_INT32_CLASS:
      OCTAVE_MAT5_INTEGER_READ (int32NDArray);
      break;

    case MAT_FILE_UINT32_CLASS:
      OCTAVE_MAT5_INTEGER_READ (uint32NDArray);
      break;

    case MAT_FILE_INT64_CLASS:
      OCTAVE_MAT5_INTEGER_READ (int64NDArray);
      break;

    case MAT_FILE_UINT64_CLASS:
      OCTAVE_MAT5_INTEGER_READ (uint64NDArray);
      break;

    case MAT_FILE_CHAR_CLASS:
      // handle as a numerical array to start with

    case MAT_FILE_DOUBLE_CLASS:
    case MAT_FILE_SINGLE_CLASS:
    default:
      {
      NDArray re (dims);
      
      // real data subelement

      std::streampos tmp_pos;
        
      if (read_mat5_tag (is, swap, type, len))
        {
          error ("load: reading matrix data for `%s'", retval.c_str ());
          goto data_read_error;
        }

      int n = re.length ();
      tmp_pos = is.tellg ();
      read_mat5_binary_data (is, re.fortran_vec (), n, swap,
                         static_cast<enum mat5_data_type> (type), flt_fmt);

      if (! is || error_state)
        {
          error ("load: reading matrix data for `%s'", retval.c_str ());
          goto data_read_error;
        }

      is.seekg (tmp_pos + static_cast<std::streamoff> (PAD (len)));

      if (logicalvar)
        {
          // Logical variables can either be MAT_FILE_UINT8_CLASS or
          // MAT_FILE_DOUBLE_CLASS, so check if we have a logical
          // variable and convert it.

          boolNDArray out (dims);
          
          for (int i = 0; i < n; i++)
            out (i) = static_cast<bool> (re (i));

          tc = out;
        }
      else if (imag)
        {
          // imaginary data subelement

          NDArray im (dims);
        
          if (read_mat5_tag (is, swap, type, len))
            {
            error ("load: reading matrix data for `%s'", retval.c_str ());
            goto data_read_error;
            }

          n = im.length ();
          read_mat5_binary_data (is, im.fortran_vec (), n, swap,
                           static_cast<enum mat5_data_type> (type), flt_fmt);

          if (! is || error_state)
            {
            error ("load: reading imaginary matrix data for `%s'",
                   retval.c_str ());
            goto data_read_error;
            }

          ComplexNDArray ctmp (dims);

          for (int i = 0; i < n; i++)
            ctmp(i) = Complex (re(i), im(i));

          tc = ctmp;
        }
      else
        {
          if (arrayclass == MAT_FILE_CHAR_CLASS)
            {
            if (type == miUTF16 || type == miUTF32)
              {
                bool found_big_char = false;
                for (int i = 0; i < n; i++)
                  {
                  if (re(i) > 127) {
                    re(i) = '?';
                    found_big_char = true;
                  }
                  }

                if (found_big_char)
                  {
                  warning ("load: can not read non-ASCII portions of UTF characters.");
                  warning ("      Replacing unreadable characters with '?'.");
                  }
              }
            else if (type == miUTF8)
              {
                // Search for multi-byte encoded UTF8 characters and
                // replace with 0x3F for '?'... Give the user a warning

                bool utf8_multi_byte = false;
                for (int i = 0; i < n; i++)
                  {
                  unsigned char a = static_cast<unsigned char> (re(i));
                  if (a > 0x7f)
                    utf8_multi_byte = true;
                  }
                
                if (utf8_multi_byte)
                  {
                  warning ("load: can not read multi-byte encoded UTF8 characters.");
                  warning ("      Replacing unreadable characters with '?'.");
                  for (int i = 0; i < n; i++)
                    {
                      unsigned char a = static_cast<unsigned char> (re(i));
                      if (a > 0x7f)
                        re(i) = '?';
                    }
                  }
              }
            tc = re;
            tc = tc.convert_to_str (false, true, '\'');
            }
          else
            tc = re;
        }
      }
    }

  is.seekg (pos + static_cast<std::streamoff> (element_length));

  if (is.eof ())
    is.clear ();

  return retval;

 data_read_error:
 early_read_error:
  error ("load: trouble reading binary file `%s'", filename.c_str ());
  return std::string ();

 skip_ahead:
  warning ("skipping over `%s'", retval.c_str ());
  is.seekg (pos + static_cast<std::streamoff> (element_length));
  return read_mat5_binary_element (is, filename, swap, global, tc);
}

int
read_mat5_binary_file_header (std::istream& is, bool& swap, bool quiet, 
                        const std::string& filename)
{
  int16_t version=0, magic=0;
  uint64_t subsys_offset;

  is.seekg (116, std::ios::beg);
  is.read (reinterpret_cast<char *> (&subsys_offset), 8);

  is.seekg (124, std::ios::beg);
  is.read (reinterpret_cast<char *> (&version), 2);
  is.read (reinterpret_cast<char *> (&magic), 2);

  if (magic == 0x4d49)
    swap = 0;
  else if (magic == 0x494d)
    swap = 1;
  else
    {
      if (! quiet)
      error ("load: can't read binary file");
      return -1;
    }

  if (! swap)                 // version number is inverse swapped!
    version = ((version >> 8) & 0xff) + ((version & 0xff) << 8);

  if (version != 1 && !quiet)
    warning ("load: found version %d binary MAT file, "
           "but only prepared for version 1", version);

  if (swap)
    swap_bytes<8> (&subsys_offset, 1);

  if (subsys_offset != 0x2020202020202020ULL && subsys_offset != 0ULL)
    {
      // Read the subsystem data block
      is.seekg (subsys_offset, std::ios::beg);

      octave_value tc;
      bool global;
      read_mat5_binary_element (is, filename, swap, global, tc);

      if (!is || error_state)
      return -1;

      if (tc.is_uint8_type ())
      {
        const uint8NDArray itmp = tc.uint8_array_value();
        octave_idx_type ilen = itmp.nelem ();

        // Why should I have to initialize outbuf as just overwrite
        std::string outbuf (ilen - 7, ' ');

        // FIXME -- find a way to avoid casting away const here
        char *ctmp = const_cast<char *> (outbuf.c_str ());
        for (octave_idx_type j = 8; j < ilen; j++)
          ctmp[j-8] = itmp(j).char_value ();

        std::istringstream fh_ws (outbuf);

        read_mat5_binary_element (fh_ws, filename, swap, global, subsys_ov);

        if (error_state)
          return -1;
      }
      else
      return -1;

      // Reposition to just after the header
      is.seekg (128, std::ios::beg);
    }

  return 0;
}

static int 
write_mat5_tag (std::ostream& is, int type, int bytes)
{
  int32_t temp;

  if (bytes > 0 && bytes <= 4)
    temp = (bytes << 16) + type;
  else
    {
      temp = type;
      if (! is.write (reinterpret_cast<char *> (&temp), 4))
      goto data_write_error;
      temp = bytes;
    }

  if (! is.write (reinterpret_cast<char *> (&temp), 4))
    goto data_write_error;

  return 0;

 data_write_error:
  return 1;
}

// write out the numeric values in M to OS,
// preceded by the appropriate tag.
static void 
write_mat5_array (std::ostream& os, const NDArray& m, bool save_as_floats)
{
  int nel = m.nelem ();
  double max_val, min_val;
  save_type st = LS_DOUBLE;
  mat5_data_type mst;
  int size;
  unsigned len;
  const double *data = m.data ();

// Have to use copy here to avoid writing over data accessed via
// Matrix::data().

#define MAT5_DO_WRITE(TYPE, data, count, stream) \
  do \
    { \
      OCTAVE_LOCAL_BUFFER (TYPE, ptr, count); \
      for (int i = 0; i < count; i++) \
        ptr[i] = static_cast<TYPE> (data[i]); \
      stream.write (reinterpret_cast<char *> (ptr), count * sizeof (TYPE)); \
    } \
  while (0)

  if (save_as_floats)
    {
      if (m.too_large_for_float ())
      {
        warning ("save: some values too large to save as floats --");
        warning ("save: saving as doubles instead");
      }
      else
      st = LS_FLOAT;
    }

  if (m.all_integers (max_val, min_val))
    st = get_save_type (max_val, min_val);

  switch (st)
    {
    default:
    case LS_DOUBLE:  mst = miDOUBLE; size = 8; break;
    case LS_FLOAT:   mst = miSINGLE; size = 4; break;
    case LS_U_CHAR:  mst = miUINT8;  size = 1; break;
    case LS_U_SHORT: mst = miUINT16; size = 2; break;
    case LS_U_INT:   mst = miUINT32; size = 4; break;
    case LS_CHAR:    mst = miINT8;   size = 1; break;
    case LS_SHORT:   mst = miINT16;  size = 2; break;
    case LS_INT:     mst = miINT32;  size = 4; break;
    }

  len = nel*size;
  write_mat5_tag (os, mst, len);

  {
    switch (st)
      {
      case LS_U_CHAR:
      MAT5_DO_WRITE (uint8_t, data, nel, os);
      break;
      
      case LS_U_SHORT:
      MAT5_DO_WRITE (uint16_t, data, nel, os);
      break;
      
      case LS_U_INT:
      MAT5_DO_WRITE (uint32_t, data, nel, os);
      break;
      
      case LS_U_LONG:
      MAT5_DO_WRITE (uint64_t, data, nel, os);
      break;

      case LS_CHAR:
      MAT5_DO_WRITE (int8_t, data, nel, os);
      break;
      
      case LS_SHORT:
      MAT5_DO_WRITE (int16_t, data, nel, os);
      break;

      case LS_INT:
      MAT5_DO_WRITE (int32_t, data, nel, os);
      break;

      case LS_LONG:
      MAT5_DO_WRITE (int64_t, data, nel, os);
      break;

      case LS_FLOAT:
      MAT5_DO_WRITE (float, data, nel, os);
      break;

      case LS_DOUBLE: // No conversion necessary.
      os.write (reinterpret_cast<const char *> (data), len);
      break;

      default:
      (*current_liboctave_error_handler)
        ("unrecognized data format requested");
      break;
      }
  }
  if (PAD (len) > len)
    {
      static char buf[9]="\x00\x00\x00\x00\x00\x00\x00\x00";
      os.write (buf, PAD (len) - len);
    }
}

template <class T>
void 
write_mat5_integer_data (std::ostream& os, const T *m, int size, int nel)
{
  mat5_data_type mst;
  unsigned len;

  switch (size)
    {
    case 1:
      mst = miUINT8;
      break;
    case 2:
      mst = miUINT16;
      break;
    case 4:
      mst = miUINT32;
      break;
    case 8:
      mst = miUINT64;
      break;
    case -1:
      mst = miINT8;
      size = - size;
      break;
    case -2:
      mst = miINT16;
      size = - size;
      break;
    case -4:
      mst = miINT32;
      size = - size;
      break;
    case -8:
    default:
      mst = miINT64;
      size = - size;
      break;
    }

  len = nel*size;
  write_mat5_tag (os, mst, len);

  os.write (reinterpret_cast<const char *> (m), len);

  if (PAD (len) > len)
    {
      static char buf[9]="\x00\x00\x00\x00\x00\x00\x00\x00";
      os.write (buf, PAD (len) - len);
    }
}

template void write_mat5_integer_data (std::ostream& os, const octave_int8 *m,
                               int size, int nel);
template void write_mat5_integer_data (std::ostream& os, const octave_int16 *m,
                               int size, int nel);
template void write_mat5_integer_data (std::ostream& os, const octave_int32 *m,
                               int size, int nel);
template void write_mat5_integer_data (std::ostream& os, const octave_int64 *m,
                               int size, int nel);
template void write_mat5_integer_data (std::ostream& os, const octave_uint8 *m,
                               int size, int nel);
template void write_mat5_integer_data (std::ostream& os, const octave_uint16 *m,
                               int size, int nel);
template void write_mat5_integer_data (std::ostream& os, const octave_uint32 *m,
                               int size, int nel);
template void write_mat5_integer_data (std::ostream& os, const octave_uint64 *m,
                               int size, int nel);
template void write_mat5_integer_data (std::ostream& os, const int *m, 
                               int size, int nel);

// Write out cell element values in the cell array to OS, preceded by
// the appropriate tag.

static bool 
write_mat5_cell_array (std::ostream& os, const Cell& cell,
                   bool mark_as_global, bool save_as_floats)
{
  int nel = cell.nelem ();

  for (int i = 0; i < nel; i++)
    {
      octave_value ov = cell(i);

      if (! save_mat5_binary_element (os, ov, "", mark_as_global,
                              false, save_as_floats))
      return false;
    }

  return true;
}

int
save_mat5_array_length (const double* val, int nel, bool save_as_floats)
{
  if (nel > 0)
    {
      int size = 8;

      if (save_as_floats)
      {
        bool too_large_for_float = false;
        for (int i = 0; i < nel; i++)
          {
            double tmp = val [i];

            if (! (xisnan (tmp) || xisinf (tmp))
              && fabs (tmp) > FLT_MAX)
            {
              too_large_for_float = true;
              break;
            }
          }

        if (!too_large_for_float)
          size = 4;
      }

      // The code below is disabled since get_save_type currently doesn't
      // deal with integer types. This will need to be activated if get_save_type
      // is changed.

      // double max_val = val[0];
      // double min_val = val[0];
      // bool all_integers =  true;
      //
      // for (int i = 0; i < nel; i++)
      //   {
      //     double val = val[i];
      //
      //     if (val > max_val)
      //       max_val = val;
      //
      //     if (val < min_val)
      //       min_val = val;
      //
      //     if (D_NINT (val) != val)
      //       {
      //         all_integers = false;
      //         break;
      //       }
      //   }
      //
      // if (all_integers)
      //   {
      //     if (max_val < 256 && min_val > -1)
      //       size = 1;
      //     else if (max_val < 65536 && min_val > -1)
      //       size = 2;
      //     else if (max_val < 4294967295UL && min_val > -1)
      //       size = 4;
      //     else if (max_val < 128 && min_val >= -128)
      //       size = 1;
      //     else if (max_val < 32768 && min_val >= -32768)
      //       size = 2;
      //     else if (max_val <= 2147483647L && min_val >= -2147483647L)
      //       size = 4;
      //   }

      return 8 + nel * size;
    }
  else
    return 8;
}

int
save_mat5_array_length (const Complex* val, int nel, bool save_as_floats)
{
  int ret;

  OCTAVE_LOCAL_BUFFER (double, tmp, nel);

  for (int i = 1; i < nel; i++)
    tmp[i] = std::real (val[i]);

  ret = save_mat5_array_length (tmp, nel, save_as_floats);

  for (int i = 1; i < nel; i++)
    tmp[i] = std::imag (val[i]);

  ret += save_mat5_array_length (tmp, nel, save_as_floats);

  return ret;
}

int
save_mat5_element_length (const octave_value& tc, const std::string& name, 
                    bool save_as_floats, bool mat7_format)
{
  int max_namelen = (mat7_format ? 63 : 31);
  int len = name.length ();
  std::string cname = tc.class_name ();
  int ret = 32;

  if (len > 4)
    ret += PAD (len > max_namelen ? max_namelen : len);

  ret += PAD (4 * tc.ndims ());
  
  if (tc.is_string ())
    {
      charNDArray chm = tc.char_array_value ();
      ret += 8;
      if (chm.nelem () > 2)
      ret += PAD (2 * chm.nelem ());
    }
  else if (tc.is_sparse_type ())
    {
      if (tc.is_complex_type ())
      {
        SparseComplexMatrix m = tc.sparse_complex_matrix_value ();
        int nc = m.cols ();
        int nnz = m.nzmax ();

        ret += 16 + PAD (nnz * sizeof (int)) + PAD ((nc + 1) * sizeof (int)) +
          save_mat5_array_length (m.data (), m.nelem (), save_as_floats);
      }
      else
      {
        SparseMatrix m = tc.sparse_matrix_value ();
        int nc = m.cols ();
        int nnz = m.nzmax ();

        ret += 16 + PAD (nnz * sizeof (int)) + PAD ((nc + 1) * sizeof (int)) +
          save_mat5_array_length (m.data (), m.nelem (), save_as_floats);
      }
    }

#define INT_LEN(nel, size) \
  { \
    ret += 8; \
    int sz = nel * size; \
    if (sz > 4) \
      ret += PAD (sz);  \
  }

  else if (cname == "int8")
    INT_LEN (tc.int8_array_value ().nelem (), 1)
  else if (cname == "int16")
    INT_LEN (tc.int16_array_value ().nelem (), 2)
  else if (cname == "int32")
    INT_LEN (tc.int32_array_value ().nelem (), 4)
  else if (cname == "int64")
    INT_LEN (tc.int64_array_value ().nelem (), 8)
  else if (cname == "uint8")
    INT_LEN (tc.uint8_array_value ().nelem (), 1)
  else if (cname == "uint16")
    INT_LEN (tc.uint16_array_value ().nelem (), 2)
  else if (cname == "uint32")
    INT_LEN (tc.uint32_array_value ().nelem (), 4)
  else if (cname == "uint64")
    INT_LEN (tc.uint64_array_value ().nelem (), 8)
  else if (tc.is_bool_type ())
    INT_LEN (tc.bool_array_value ().nelem (), 1)
  else if (tc.is_real_scalar () || tc.is_real_matrix () || tc.is_range ())
    {
      NDArray m = tc.array_value ();
      ret += save_mat5_array_length (m.fortran_vec (), m.nelem (),
                             save_as_floats);
    }
  else if (tc.is_cell ())
    {
      Cell cell = tc.cell_value ();
      int nel = cell.nelem ();

      for (int i = 0; i < nel; i++)
      ret += 8 + 
        save_mat5_element_length (cell (i), "", save_as_floats, mat7_format);
    }
  else if (tc.is_complex_scalar () || tc.is_complex_matrix ()) 
    {
      ComplexNDArray m = tc.complex_array_value ();
      ret += save_mat5_array_length (m.fortran_vec (), m.nelem (),
                             save_as_floats);
    }
  else if (tc.is_map () || tc.is_inline_function ()) 
    {
      int fieldcnt = 0;
      const Octave_map m = tc.map_value ();
      int nel = m.numel ();

      if (tc.is_inline_function ())
      // length of "inline" is 6
      ret += 8 + PAD (6 > max_namelen ? max_namelen : 6);

      for (Octave_map::const_iterator i = m.begin (); i != m.end (); i++)
      fieldcnt++;

      ret += 16 + fieldcnt * (max_namelen + 1);


      for (int j = 0; j < nel; j++)
      {

        for (Octave_map::const_iterator i = m.begin (); i != m.end (); i++)
          {
            Cell elts = m.contents (i);

            ret += 8 + save_mat5_element_length (elts (j), "", 
                                     save_as_floats, mat7_format);
          }
      }
    }
  else
    ret = -1;

  return ret;
}

// save the data from TC along with the corresponding NAME on stream
// OS in the MatLab version 5 binary format.  Return true on success.

bool
save_mat5_binary_element (std::ostream& os,
                    const octave_value& tc, const std::string& name,
                    bool mark_as_global, bool mat7_format,
                    bool save_as_floats, bool compressing) 
{
  int32_t flags=0;
  int32_t nnz=0;
  std::streampos fixup, contin;
  std::string cname = tc.class_name ();
  int max_namelen = (mat7_format ? 63 : 31);

#ifdef HAVE_ZLIB
  if (mat7_format && !compressing)
    {
      bool ret = false;

      std::ostringstream buf;

      // The code seeks backwards in the stream to fix the header. Can't
      // do this with zlib, so use a stringstream.
      ret = save_mat5_binary_element (buf, tc, name, mark_as_global, true,
                              save_as_floats, true);

      if (ret)
      {
        // destLen must be at least 0.1% larger than source buffer 
        // + 12 bytes. Reality is it must be larger again than that.
        std::string buf_str = buf.str ();
        uLongf srcLen = buf_str.length ();
        uLongf destLen = srcLen * 101 / 100 + 12; 
        OCTAVE_LOCAL_BUFFER (char, out_buf, destLen);

        if (compress (reinterpret_cast<Bytef *> (out_buf), &destLen, 
                  reinterpret_cast<const Bytef *> (buf_str.c_str ()), srcLen) == Z_OK)
          {
            write_mat5_tag (os, miCOMPRESSED, static_cast<int> (destLen)); 
            os.write (out_buf, destLen);
          }
        else
          {
            error ("save: error compressing data element");
            ret = false;
          }
      }

      return ret;
    }
#endif

  // element type and length
  fixup = os.tellp ();
  write_mat5_tag (os, miMATRIX, save_mat5_element_length 
              (tc, name, save_as_floats, mat7_format));
  
  // array flags subelement
  write_mat5_tag (os, miUINT32, 8);

  if (tc.is_bool_type ())
    flags |= 0x0200;

  if (mark_as_global)
    flags |= 0x0400;

  if (tc.is_complex_scalar () || tc.is_complex_matrix ())
    flags |= 0x0800;

  if (tc.is_string ())
    flags |= MAT_FILE_CHAR_CLASS;
  else if (cname == "int8")
    flags |= MAT_FILE_INT8_CLASS;
  else if (cname == "int16")
    flags |= MAT_FILE_INT16_CLASS;
  else if (cname == "int32")
    flags |= MAT_FILE_INT32_CLASS;
  else if (cname == "int64")
    flags |= MAT_FILE_INT64_CLASS;
  else if (cname == "uint8" || tc.is_bool_type ())
    flags |= MAT_FILE_UINT8_CLASS;
  else if (cname == "uint16")
    flags |= MAT_FILE_UINT16_CLASS;
  else if (cname == "uint32")
    flags |= MAT_FILE_UINT32_CLASS;
  else if (cname == "uint64")
    flags |= MAT_FILE_UINT64_CLASS;
  else if (tc.is_sparse_type ())
    {
      flags |= MAT_FILE_SPARSE_CLASS;
      if (tc.is_complex_type ())
      {
        SparseComplexMatrix scm = tc.sparse_complex_matrix_value ();
        nnz = scm.nzmax ();
      }
      else
      {
        SparseMatrix sm = tc.sparse_matrix_value ();
        nnz = sm.nzmax ();
      }
    }
  else if (tc.is_real_scalar ())
    flags |= MAT_FILE_DOUBLE_CLASS;
  else if (tc.is_real_matrix () || tc.is_range ())
    flags |= MAT_FILE_DOUBLE_CLASS;
  else if (tc.is_complex_scalar ())
    flags |= MAT_FILE_DOUBLE_CLASS;
  else if (tc.is_complex_matrix ())
    flags |= MAT_FILE_DOUBLE_CLASS;
  else if (tc.is_map ()) 
    flags |= MAT_FILE_STRUCT_CLASS;
  else if (tc.is_cell ())
    flags |= MAT_FILE_CELL_CLASS;
  else if (tc.is_inline_function ())
    flags |= MAT_FILE_OBJECT_CLASS;
  else
    {
      gripe_wrong_type_arg ("save", tc, false);
      goto error_cleanup;
    }

  os.write (reinterpret_cast<char *> (&flags), 4);
  os.write (reinterpret_cast<char *> (&nnz), 4);

  {
    dim_vector dv = tc.dims ();
    int nd = tc.ndims ();
    int dim_len = 4*nd;

    write_mat5_tag (os, miINT32, dim_len);

    for (int i = 0; i < nd; i++)
      {
      int32_t n = dv(i);
      os.write (reinterpret_cast<char *> (&n), 4);
      }

    if (PAD (dim_len) > dim_len)
      {
      static char buf[9]="\x00\x00\x00\x00\x00\x00\x00\x00";
      os.write (buf, PAD (dim_len) - dim_len);
      }
  }

  // array name subelement
  {
    int namelen = name.length ();

    if (namelen > max_namelen)
      namelen = max_namelen; // only 31 or 63 char names permitted in mat file

    int paddedlength = PAD (namelen);

    write_mat5_tag (os, miINT8, namelen);
    OCTAVE_LOCAL_BUFFER (char, paddedname, paddedlength);
    memset (paddedname, 0, paddedlength);
    strncpy (paddedname, name.c_str (), namelen);
    os.write (paddedname, paddedlength);
  }

  // data element
  if (tc.is_string ())
    {
      charNDArray chm = tc.char_array_value ();
      int nel = chm.nelem ();
      int len = nel*2;
      int paddedlength = PAD (len);

      OCTAVE_LOCAL_BUFFER (int16_t, buf, nel+3);
      write_mat5_tag (os, miUINT16, len);

      const char *s = chm.data ();

      for (int i = 0; i < nel; i++)
      buf[i] = *s++ & 0x00FF;

      os.write (reinterpret_cast<char *> (buf), len);
      
      if (paddedlength > len)
      {
        static char padbuf[9]="\x00\x00\x00\x00\x00\x00\x00\x00";
        os.write (padbuf, paddedlength - len);
      }
    }
  else if (tc.is_sparse_type ())
    {
      if (tc.is_complex_type ())
      {
        SparseComplexMatrix m = tc.sparse_complex_matrix_value ();
        int nc = m.cols ();

        int tmp = sizeof (int);

        write_mat5_integer_data (os, m.ridx (), -tmp, nnz);
        write_mat5_integer_data (os, m.cidx (), -tmp, nc + 1);

        NDArray buf (dim_vector (nnz, 1));

        for (int i = 0; i < nnz; i++)
          buf (i) = std::real (m.data (i));

        write_mat5_array (os, buf, save_as_floats);

        for (int i = 0; i < nnz; i++)
          buf (i) = std::imag (m.data (i));

        write_mat5_array (os, buf, save_as_floats);
      }
      else
      {
        SparseMatrix m = tc.sparse_matrix_value ();
        int nc = m.cols ();

        int tmp = sizeof (int);

        write_mat5_integer_data (os, m.ridx (), -tmp, nnz);
        write_mat5_integer_data (os, m.cidx (), -tmp, nc + 1);

        // FIXME
        // Is there a way to easily do without this buffer
        NDArray buf (dim_vector (nnz, 1));

        for (int i = 0; i < nnz; i++)
          buf (i) = m.data (i);

        write_mat5_array (os, buf, save_as_floats);
      }
    }
  else if (cname == "int8")
    {
      int8NDArray m = tc.int8_array_value ();

      write_mat5_integer_data (os, m.fortran_vec (), -1, m.nelem ());
    }
  else if (cname == "int16")
    {
      int16NDArray m = tc.int16_array_value ();

      write_mat5_integer_data (os, m.fortran_vec (), -2, m.nelem ());
    }
  else if (cname == "int32")
    {
      int32NDArray m = tc.int32_array_value ();

      write_mat5_integer_data (os, m.fortran_vec (), -4, m.nelem ());
    }
  else if (cname == "int64")
    {
      int64NDArray m = tc.int64_array_value ();

      write_mat5_integer_data (os, m.fortran_vec (), -8, m.nelem ());
    }
  else if (cname == "uint8")
    {
      uint8NDArray m = tc.uint8_array_value ();

      write_mat5_integer_data (os, m.fortran_vec (), 1, m.nelem ());
    }
  else if (cname == "uint16")
    {
      uint16NDArray m = tc.uint16_array_value ();

      write_mat5_integer_data (os, m.fortran_vec (), 2, m.nelem ());
    }
  else if (cname == "uint32")
    {
      uint32NDArray m = tc.uint32_array_value ();

      write_mat5_integer_data (os, m.fortran_vec (), 4, m.nelem ());
    }
  else if (cname == "uint64")
    {
      uint64NDArray m = tc.uint64_array_value ();

      write_mat5_integer_data (os, m.fortran_vec (), 8, m.nelem ());
    }
  else if (tc.is_bool_type ())
    {
      uint8NDArray m (tc.bool_array_value ());

      write_mat5_integer_data (os, m.fortran_vec (), 1, m.nelem ());
    }
  else if (tc.is_real_scalar () || tc.is_real_matrix () || tc.is_range ())
    {
      NDArray m = tc.array_value ();

      write_mat5_array (os, m, save_as_floats);
    }
  else if (tc.is_cell ())
    {
      Cell cell = tc.cell_value ();

      if (! write_mat5_cell_array (os, cell, mark_as_global, save_as_floats))
      goto error_cleanup;
    }
  else if (tc.is_complex_scalar () || tc.is_complex_matrix ()) 
    {
      ComplexNDArray m_cmplx = tc.complex_array_value ();

      write_mat5_array (os, ::real (m_cmplx), save_as_floats);
      write_mat5_array (os, ::imag (m_cmplx), save_as_floats);
    }
  else if (tc.is_map () || tc.is_inline_function()) 
    {
      const Octave_map m = tc.map_value ();
      if (tc.is_inline_function ())
      {
        std::string classname = "inline";
        int namelen = classname.length ();

        if (namelen > max_namelen)
          namelen = max_namelen; // only 31 or 63 char names permitted

        int paddedlength = PAD (namelen);

        write_mat5_tag (os, miINT8, namelen);
        OCTAVE_LOCAL_BUFFER (char, paddedname, paddedlength);
        memset (paddedname, 0, paddedlength);
        strncpy (paddedname, classname.c_str (), namelen);
        os.write (paddedname, paddedlength);
      }

      // an Octave structure */
      // recursively write each element of the structure
      {
      char buf[64];
      int32_t maxfieldnamelength = max_namelen + 1;
      int fieldcnt = 0;

      for (Octave_map::const_iterator i = m.begin (); i != m.end (); i++)
        fieldcnt++;

      write_mat5_tag (os, miINT32, 4);
      os.write (reinterpret_cast<char *> (&maxfieldnamelength), 4);
      write_mat5_tag (os, miINT8, fieldcnt*maxfieldnamelength);

      for (Octave_map::const_iterator i = m.begin (); i != m.end (); i++)
        {
          // write the name of each element
          std::string tstr = m.key (i);
          memset (buf, 0, max_namelen + 1);
          strncpy (buf, tstr.c_str (), max_namelen); // only 31 or 63 char names permitted
          os.write (buf, max_namelen + 1);
        }

      int len = m.numel ();

      for (int j = 0; j < len; j++)
        {
          // write the data of each element

          for (Octave_map::const_iterator i = m.begin (); i != m.end (); i++)
            {
            Cell elts = m.contents (i);

            bool retval2 = save_mat5_binary_element (os, elts(j), "",
                                           mark_as_global,
                                           false, 
                                           save_as_floats);
            if (! retval2)
              goto error_cleanup;
            }
        }
      }
    }
  else
    gripe_wrong_type_arg ("save", tc, false);

  contin = os.tellp ();

  return true;

 error_cleanup:
  error ("save: error while writing `%s' to MAT file", name.c_str ());

  return false;
}

/*
;;; Local Variables: ***
;;; mode: C++ ***
;;; End: ***
*/


Generated by  Doxygen 1.6.0   Back to index