GNU Octave  4.0.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
ls-mat5.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 1996-2015 John W. Eaton
4 
5 This file is part of Octave.
6 
7 Octave is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Octave; see the file COPYING. If not, see
19 <http://www.gnu.org/licenses/>.
20 
21 */
22 
23 // Author: James R. Van Zandt <[email protected]>
24 
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28 
29 #include <cfloat>
30 #include <cstring>
31 #include <cctype>
32 
33 #include <fstream>
34 #include <iomanip>
35 #include <iostream>
36 #include <sstream>
37 #include <string>
38 #include <vector>
39 
40 #include "byte-swap.h"
41 #include "data-conv.h"
42 #include "file-ops.h"
43 #include "glob-match.h"
44 #include "lo-mappers.h"
45 #include "mach-info.h"
46 #include "oct-env.h"
47 #include "oct-time.h"
48 #include "quit.h"
49 #include "str-vec.h"
50 #include "file-stat.h"
51 #include "oct-locbuf.h"
52 
53 #include "Cell.h"
54 #include "defun.h"
55 #include "error.h"
56 #include "gripes.h"
57 #include "load-save.h"
58 #include "load-path.h"
59 #include "oct-obj.h"
60 #include "oct-map.h"
61 #include "ov-cell.h"
62 #include "ov-class.h"
63 #include "ov-fcn-inline.h"
64 #include "pager.h"
65 #include "pt-exp.h"
66 #include "sysdep.h"
67 #include "toplev.h"
68 #include "unwind-prot.h"
69 #include "utils.h"
70 #include "variables.h"
71 #include "version.h"
72 #include "dMatrix.h"
73 
74 #include "ls-utils.h"
75 #include "ls-mat5.h"
76 
77 #include "parse.h"
78 #include "defaults.h"
79 
80 #ifdef HAVE_ZLIB
81 #include <zlib.h>
82 #endif
83 
84 #define READ_PAD(is_small_data_element, l) ((is_small_data_element) ? 4 : (((l)+7)/8)*8)
85 #define PAD(l) (((l) > 0 && (l) <= 4) ? 4 : (((l)+7)/8)*8)
86 #define INT8(l) ((l) == miINT8 || (l) == miUINT8 || (l) == miUTF8)
87 
88 
89 // The subsystem data block
91 
92 // FIXME: the following enum values should be the same as the
93 // mxClassID values in mexproto.h, but it seems they have also changed
94 // over time. What is the correct way to handle this and maintain
95 // backward compatibility with old MAT files? For now, use
96 // "MAT_FILE_" instead of "mx" as the prefix for these names to avoid
97 // conflict with the mxClassID enum in mexproto.h.
98 
100 {
101  MAT_FILE_CELL_CLASS=1, // cell array
102  MAT_FILE_STRUCT_CLASS, // structure
104  MAT_FILE_CHAR_CLASS, // character array
105  MAT_FILE_SPARSE_CLASS, // sparse array
106  MAT_FILE_DOUBLE_CLASS, // double precision array
107  MAT_FILE_SINGLE_CLASS, // single precision floating point
108  MAT_FILE_INT8_CLASS, // 8 bit signed integer
109  MAT_FILE_UINT8_CLASS, // 8 bit unsigned integer
110  MAT_FILE_INT16_CLASS, // 16 bit signed integer
111  MAT_FILE_UINT16_CLASS, // 16 bit unsigned integer
112  MAT_FILE_INT32_CLASS, // 32 bit signed integer
113  MAT_FILE_UINT32_CLASS, // 32 bit unsigned integer
114  MAT_FILE_INT64_CLASS, // 64 bit signed integer
115  MAT_FILE_UINT64_CLASS, // 64 bit unsigned integer
116  MAT_FILE_FUNCTION_CLASS, // Function handle
117  MAT_FILE_WORKSPACE_CLASS // Workspace (undocumented)
118 };
119 
120 // Read COUNT elements of data from IS in the format specified by TYPE,
121 // placing the result in DATA. If SWAP is TRUE, swap the bytes of
122 // each element before copying to DATA. FLT_FMT specifies the format
123 // of the data if we are reading floating point numbers.
124 
125 static void
126 read_mat5_binary_data (std::istream& is, double *data,
127  octave_idx_type count, bool swap, mat5_data_type type,
129 {
130 
131  switch (type)
132  {
133  case miINT8:
134  read_doubles (is, data, LS_CHAR, count, swap, flt_fmt);
135  break;
136 
137  case miUTF8:
138  case miUINT8:
139  read_doubles (is, data, LS_U_CHAR, count, swap, flt_fmt);
140  break;
141 
142  case miINT16:
143  read_doubles (is, data, LS_SHORT, count, swap, flt_fmt);
144  break;
145 
146  case miUTF16:
147  case miUINT16:
148  read_doubles (is, data, LS_U_SHORT, count, swap, flt_fmt);
149  break;
150 
151  case miINT32:
152  read_doubles (is, data, LS_INT, count, swap, flt_fmt);
153  break;
154 
155  case miUTF32:
156  case miUINT32:
157  read_doubles (is, data, LS_U_INT, count, swap, flt_fmt);
158  break;
159 
160  case miSINGLE:
161  read_doubles (is, data, LS_FLOAT, count, swap, flt_fmt);
162  break;
163 
164  case miRESERVE1:
165  break;
166 
167  case miDOUBLE:
168  read_doubles (is, data, LS_DOUBLE, count, swap, flt_fmt);
169  break;
170 
171  case miRESERVE2:
172  case miRESERVE3:
173  break;
174 
175  // FIXME: how are the 64-bit cases supposed to work here?
176  case miINT64:
177  read_doubles (is, data, LS_LONG, count, swap, flt_fmt);
178  break;
179 
180  case miUINT64:
181  read_doubles (is, data, LS_U_LONG, count, swap, flt_fmt);
182  break;
183 
184  case miMATRIX:
185  default:
186  break;
187  }
188 }
189 
190 static void
191 read_mat5_binary_data (std::istream& is, float *data,
192  octave_idx_type count, bool swap, mat5_data_type type,
194 {
195 
196  switch (type)
197  {
198  case miINT8:
199  read_floats (is, data, LS_CHAR, count, swap, flt_fmt);
200  break;
201 
202  case miUTF8:
203  case miUINT8:
204  read_floats (is, data, LS_U_CHAR, count, swap, flt_fmt);
205  break;
206 
207  case miINT16:
208  read_floats (is, data, LS_SHORT, count, swap, flt_fmt);
209  break;
210 
211  case miUTF16:
212  case miUINT16:
213  read_floats (is, data, LS_U_SHORT, count, swap, flt_fmt);
214  break;
215 
216  case miINT32:
217  read_floats (is, data, LS_INT, count, swap, flt_fmt);
218  break;
219 
220  case miUTF32:
221  case miUINT32:
222  read_floats (is, data, LS_U_INT, count, swap, flt_fmt);
223  break;
224 
225  case miSINGLE:
226  read_floats (is, data, LS_FLOAT, count, swap, flt_fmt);
227  break;
228 
229  case miRESERVE1:
230  break;
231 
232  case miDOUBLE:
233  read_floats (is, data, LS_DOUBLE, count, swap, flt_fmt);
234  break;
235 
236  case miRESERVE2:
237  case miRESERVE3:
238  break;
239 
240  // FIXME: how are the 64-bit cases supposed to work here?
241  case miINT64:
242  read_floats (is, data, LS_LONG, count, swap, flt_fmt);
243  break;
244 
245  case miUINT64:
246  read_floats (is, data, LS_U_LONG, count, swap, flt_fmt);
247  break;
248 
249  case miMATRIX:
250  default:
251  break;
252  }
253 }
254 
255 template <class T>
256 void
257 read_mat5_integer_data (std::istream& is, T *m, octave_idx_type count,
258  bool swap, mat5_data_type type)
259 {
260 
261 #define READ_INTEGER_DATA(TYPE, swap, data, size, len, stream) \
262  do \
263  { \
264  if (len > 0) \
265  { \
266  OCTAVE_LOCAL_BUFFER (TYPE, ptr, len); \
267  std::streamsize n_bytes = size * static_cast<std::streamsize> (len); \
268  stream.read (reinterpret_cast<char *> (ptr), n_bytes); \
269  if (swap) \
270  swap_bytes< size > (ptr, len); \
271  for (octave_idx_type i = 0; i < len; i++) \
272  data[i] = ptr[i]; \
273  } \
274  } \
275  while (0)
276 
277  switch (type)
278  {
279  case miINT8:
280  READ_INTEGER_DATA (int8_t, swap, m, 1, count, is);
281  break;
282 
283  case miUINT8:
284  READ_INTEGER_DATA (uint8_t, swap, m, 1, count, is);
285  break;
286 
287  case miINT16:
288  READ_INTEGER_DATA (int16_t, swap, m, 2, count, is);
289  break;
290 
291  case miUINT16:
292  READ_INTEGER_DATA (uint16_t, swap, m, 2, count, is);
293  break;
294 
295  case miINT32:
296  READ_INTEGER_DATA (int32_t, swap, m, 4, count, is);
297  break;
298 
299  case miUINT32:
300  READ_INTEGER_DATA (uint32_t, swap, m, 4, count, is);
301  break;
302 
303  case miSINGLE:
304  case miRESERVE1:
305  case miDOUBLE:
306  case miRESERVE2:
307  case miRESERVE3:
308  break;
309 
310  case miINT64:
311  READ_INTEGER_DATA (int64_t, swap, m, 8, count, is);
312  break;
313 
314  case miUINT64:
315  READ_INTEGER_DATA (uint64_t, swap, m, 8, count, is);
316  break;
317 
318  case miMATRIX:
319  default:
320  break;
321  }
322 
323 #undef READ_INTEGER_DATA
324 
325 }
326 
327 template void
328 read_mat5_integer_data (std::istream& is, octave_int8 *m,
329  octave_idx_type count, bool swap,
331 
332 template void
333 read_mat5_integer_data (std::istream& is, octave_int16 *m,
334  octave_idx_type count, bool swap,
336 
337 template void
338 read_mat5_integer_data (std::istream& is, octave_int32 *m,
339  octave_idx_type count, bool swap,
341 
342 template void
343 read_mat5_integer_data (std::istream& is, octave_int64 *m,
344  octave_idx_type count, bool swap,
346 
347 template void
348 read_mat5_integer_data (std::istream& is, octave_uint8 *m,
349  octave_idx_type count, bool swap,
351 
352 template void
353 read_mat5_integer_data (std::istream& is, octave_uint16 *m,
354  octave_idx_type count, bool swap,
356 
357 template void
358 read_mat5_integer_data (std::istream& is, octave_uint32 *m,
359  octave_idx_type count, bool swap,
361 
362 template void
363 read_mat5_integer_data (std::istream& is, octave_uint64 *m,
364  octave_idx_type count, bool swap,
366 
367 template void
368 read_mat5_integer_data (std::istream& is, int *m,
369  octave_idx_type count, bool swap,
371 
372 #define OCTAVE_MAT5_INTEGER_READ(TYP) \
373  { \
374  TYP re (dims); \
375  \
376  std::streampos tmp_pos; \
377  \
378  if (read_mat5_tag (is, swap, type, len, is_small_data_element)) \
379  { \
380  error ("load: reading matrix data for '%s'", retval.c_str ()); \
381  goto data_read_error; \
382  } \
383  \
384  octave_idx_type n = re.numel (); \
385  tmp_pos = is.tellg (); \
386  read_mat5_integer_data (is, re.fortran_vec (), n, swap, \
387  static_cast<enum mat5_data_type> (type)); \
388  \
389  if (! is || error_state) \
390  { \
391  error ("load: reading matrix data for '%s'", retval.c_str ()); \
392  goto data_read_error; \
393  } \
394  \
395  is.seekg (tmp_pos + static_cast<std::streamoff>\
396  (READ_PAD (is_small_data_element, len))); \
397  \
398  if (imag) \
399  { \
400  /* We don't handle imag integer types, convert to an array */ \
401  NDArray im (dims); \
402  \
403  if (read_mat5_tag (is, swap, type, len, is_small_data_element)) \
404  { \
405  error ("load: reading matrix data for '%s'", \
406  retval.c_str ()); \
407  goto data_read_error; \
408  } \
409  \
410  n = im.numel (); \
411  read_mat5_binary_data (is, im.fortran_vec (), n, swap, \
412  static_cast<enum mat5_data_type> (type), flt_fmt); \
413  \
414  if (! is || error_state) \
415  { \
416  error ("load: reading imaginary matrix data for '%s'", \
417  retval.c_str ()); \
418  goto data_read_error; \
419  } \
420  \
421  ComplexNDArray ctmp (dims); \
422  \
423  for (octave_idx_type i = 0; i < n; i++) \
424  ctmp(i) = Complex (re(i).double_value (), im(i)); \
425  \
426  tc = ctmp; \
427  } \
428  else \
429  tc = re; \
430  }
431 
432 // Read one element tag from stream IS,
433 // place the type code in TYPE, the byte count in BYTES and true (false) to
434 // IS_SMALL_DATA_ELEMENT if the tag is 4 (8) bytes long.
435 // return nonzero on error
436 static int
437 read_mat5_tag (std::istream& is, bool swap, int32_t& type, int32_t& bytes,
438  bool& is_small_data_element)
439 {
440  unsigned int upper;
441  int32_t temp;
442 
443  if (! is.read (reinterpret_cast<char *> (&temp), 4))
444  goto data_read_error;
445 
446  if (swap)
447  swap_bytes<4> (&temp);
448 
449  upper = (temp >> 16) & 0xffff;
450  type = temp & 0xffff;
451 
452  if (upper)
453  {
454  // "compressed" format
455  bytes = upper;
456  is_small_data_element = true;
457  }
458  else
459  {
460  if (! is.read (reinterpret_cast<char *> (&temp), 4))
461  goto data_read_error;
462  if (swap)
463  swap_bytes<4> (&temp);
464  bytes = temp;
465  is_small_data_element = false;
466  }
467 
468  return 0;
469 
470 data_read_error:
471  return 1;
472 }
473 
474 static void
475 read_int (std::istream& is, bool swap, int32_t& val)
476 {
477  is.read (reinterpret_cast<char *> (&val), 4);
478 
479  if (swap)
480  swap_bytes<4> (&val);
481 }
482 
483 // Extract one data element (scalar, matrix, string, etc.) from stream
484 // IS and place it in TC, returning the name of the variable.
485 //
486 // The data is expected to be in Matlab's "Version 5" .mat format,
487 // though not all the features of that format are supported.
488 //
489 // FILENAME is used for error messages.
490 
491 std::string
492 read_mat5_binary_element (std::istream& is, const std::string& filename,
493  bool swap, bool& global, octave_value& tc)
494 {
495  std::string retval;
496 
497  global = false;
498 
499  // NOTE: these are initialized here instead of closer to where they
500  // are first used to avoid errors from gcc about goto crossing
501  // initialization of variable.
502 
503  bool imag;
504  bool isclass = false;
505  bool logicalvar;
506  dim_vector dims;
507  enum arrayclasstype arrayclass;
508  int16_t number = *(reinterpret_cast<const int16_t *>("\x00\x01"));
509  octave_idx_type nzmax;
510  std::string classname;
511 
512  // MAT files always use IEEE floating point
514  if ((number == 1) ^ swap)
516  else
518 
519  // element type, length and small data element flag
520  int32_t type = 0;
521  int32_t element_length;
522  bool is_small_data_element;
523  if (read_mat5_tag (is, swap, type, element_length, is_small_data_element))
524  return retval; // EOF
525 
526  if (type == miCOMPRESSED)
527  {
528 #ifdef HAVE_ZLIB
529  // If C++ allowed us direct access to the file descriptor of an
530  // ifstream in a uniform way, the code below could be vastly
531  // simplified, and additional copies of the data in memory
532  // wouldn't be needed.
533 
534  OCTAVE_LOCAL_BUFFER (char, inbuf, element_length);
535  is.read (inbuf, element_length);
536 
537  // We uncompress the first 8 bytes of the header to get the buffer length
538  // This will fail with an error Z_MEM_ERROR
539  uLongf destLen = 8;
540  OCTAVE_LOCAL_BUFFER (unsigned int, tmp, 2);
541  if (uncompress (reinterpret_cast<Bytef *> (tmp), &destLen,
542  reinterpret_cast<Bytef *> (inbuf), element_length)
543  != Z_MEM_ERROR)
544  {
545  // Why should I have to initialize outbuf as I'll just overwrite!!
546  if (swap)
547  swap_bytes<4> (tmp, 2);
548 
549  destLen = tmp[1] + 8;
550  std::string outbuf (destLen, ' ');
551 
552  // FIXME: find a way to avoid casting away const here!
553 
554  int err = uncompress (reinterpret_cast<Bytef *>
555  (const_cast<char *> (outbuf.c_str ())),
556  &destLen, reinterpret_cast<Bytef *> (inbuf),
557  element_length);
558 
559  if (err != Z_OK)
560  {
561  std::string msg;
562  switch (err)
563  {
564  case Z_STREAM_END:
565  msg = "stream end";
566  break;
567 
568  case Z_NEED_DICT:
569  msg = "need dict";
570  break;
571 
572  case Z_ERRNO:
573  msg = "errno case";
574  break;
575 
576  case Z_STREAM_ERROR:
577  msg = "stream error";
578  break;
579 
580  case Z_DATA_ERROR:
581  msg = "data error";
582  break;
583 
584  case Z_MEM_ERROR:
585  msg = "mem error";
586  break;
587 
588  case Z_BUF_ERROR:
589  msg = "buf error";
590  break;
591 
592  case Z_VERSION_ERROR:
593  msg = "version error";
594  break;
595  }
596 
597  error ("load: error uncompressing data element (%s from zlib)",
598  msg.c_str ());
599  }
600  else
601  {
602  std::istringstream gz_is (outbuf);
603  retval = read_mat5_binary_element (gz_is, filename,
604  swap, global, tc);
605  }
606  }
607  else
608  error ("load: error probing size of compressed data element");
609 
610  return retval;
611 #else // HAVE_ZLIB
612  error ("load: zlib unavailable, cannot read compressed data element");
613 #endif
614  }
615 
616  std::streampos pos;
617 
618  if (type != miMATRIX)
619  {
620  pos = is.tellg ();
621  error ("load: invalid element type = %d", type);
622  goto early_read_error;
623  }
624 
625  if (element_length == 0)
626  {
627  tc = Matrix ();
628  return retval;
629  }
630 
631  pos = is.tellg ();
632 
633  // array flags subelement
634  int32_t len;
635  if (read_mat5_tag (is, swap, type, len, is_small_data_element)
636  || type != miUINT32 || len != 8 || is_small_data_element)
637  {
638  error ("load: invalid array flags subelement");
639  goto early_read_error;
640  }
641 
642  int32_t flags;
643  read_int (is, swap, flags);
644 
645  imag = (flags & 0x0800) != 0; // has an imaginary part?
646 
647  global = (flags & 0x0400) != 0; // global variable?
648 
649  logicalvar = (flags & 0x0200) != 0; // boolean ?
650 
651  arrayclass = static_cast<arrayclasstype> (flags & 0xff);
652 
653  int32_t tmp_nzmax;
654  read_int (is, swap, tmp_nzmax); // max number of nonzero in sparse
655  nzmax = tmp_nzmax;
656 
657  // dimensions array subelement
658  if (arrayclass != MAT_FILE_WORKSPACE_CLASS)
659  {
660  int32_t dim_len;
661 
662  if (read_mat5_tag (is, swap, type, dim_len, is_small_data_element)
663  || type != miINT32)
664  {
665  error ("load: invalid dimensions array subelement");
666  goto early_read_error;
667  }
668 
669  int ndims = dim_len / 4;
670  if (ndims == 1)
671  {
672  // R and Python can create a 1-D object which is really an Nx1 object
673  dims.resize (2);
674  dims(1) = 1;
675  }
676  else
677  dims.resize (ndims);
678 
679  for (int i = 0; i < ndims; i++)
680  {
681  int32_t n;
682  read_int (is, swap, n);
683  dims(i) = n;
684  }
685 
686  std::streampos tmp_pos = is.tellg ();
687  is.seekg (tmp_pos + static_cast<std::streamoff>
688  (READ_PAD (is_small_data_element, dim_len) - dim_len));
689  }
690  else
691  {
692  // Why did mathworks decide to not have dims for a workspace!!!
693  dims.resize (2);
694  dims(0) = 1;
695  dims(1) = 1;
696  }
697 
698  if (read_mat5_tag (is, swap, type, len, is_small_data_element)
699  || ! INT8(type))
700  {
701  error ("load: invalid array name subelement");
702  goto early_read_error;
703  }
704 
705  {
706  OCTAVE_LOCAL_BUFFER (char, name, len+1);
707 
708  // Structure field subelements have zero-length array name subelements.
709 
710  std::streampos tmp_pos = is.tellg ();
711 
712  if (len)
713  {
714  if (! is.read (name, len))
715  goto data_read_error;
716 
717  is.seekg (tmp_pos + static_cast<std::streamoff>
718  (READ_PAD (is_small_data_element, len)));
719  }
720 
721  name[len] = '\0';
722  retval = name;
723  }
724 
725  switch (arrayclass)
726  {
727  case MAT_FILE_CELL_CLASS:
728  {
729  Cell cell_array (dims);
730 
731  octave_idx_type n = cell_array.numel ();
732 
733  for (octave_idx_type i = 0; i < n; i++)
734  {
735  octave_value tc2;
736 
737  std::string nm
738  = read_mat5_binary_element (is, filename, swap, global, tc2);
739 
740  if (! is || error_state)
741  {
742  error ("load: reading cell data for '%s'", nm.c_str ());
743  goto data_read_error;
744  }
745 
746  cell_array(i) = tc2;
747  }
748 
749  tc = cell_array;
750  }
751  break;
752 
754  {
755  octave_idx_type nr = dims(0);
756  octave_idx_type nc = dims(1);
757  SparseMatrix sm;
759  octave_idx_type *ridx;
760  octave_idx_type *cidx;
761  double *data;
762 
763  // Setup return value
764  if (imag)
765  {
766  scm = SparseComplexMatrix (nr, nc, nzmax);
767  ridx = scm.ridx ();
768  cidx = scm.cidx ();
769  data = 0;
770  }
771  else
772  {
773  sm = SparseMatrix (nr, nc, nzmax);
774  ridx = sm.ridx ();
775  cidx = sm.cidx ();
776  data = sm.data ();
777  }
778 
779  // row indices
780  std::streampos tmp_pos;
781 
782  if (read_mat5_tag (is, swap, type, len, is_small_data_element))
783  {
784  error ("load: reading sparse row data for '%s'", retval.c_str ());
785  goto data_read_error;
786  }
787 
788  tmp_pos = is.tellg ();
789 
790  read_mat5_integer_data (is, ridx, nzmax, swap,
791  static_cast<enum mat5_data_type> (type));
792 
793  if (! is || error_state)
794  {
795  error ("load: reading sparse row data for '%s'", retval.c_str ());
796  goto data_read_error;
797  }
798 
799  is.seekg (tmp_pos + static_cast<std::streamoff>
800  (READ_PAD (is_small_data_element, len)));
801 
802  // col indices
803  if (read_mat5_tag (is, swap, type, len, is_small_data_element))
804  {
805  error ("load: reading sparse column data for '%s'",
806  retval.c_str ());
807  goto data_read_error;
808  }
809 
810  tmp_pos = is.tellg ();
811 
812  read_mat5_integer_data (is, cidx, nc + 1, swap,
813  static_cast<enum mat5_data_type> (type));
814 
815  if (! is || error_state)
816  {
817  error ("load: reading sparse column data for '%s'",
818  retval.c_str ());
819  goto data_read_error;
820  }
821 
822  is.seekg (tmp_pos + static_cast<std::streamoff>
823  (READ_PAD (is_small_data_element, len)));
824 
825  // real data subelement
826  if (read_mat5_tag (is, swap, type, len, is_small_data_element))
827  {
828  error ("load: reading sparse matrix data for '%s'",
829  retval.c_str ());
830  goto data_read_error;
831  }
832 
833  octave_idx_type nnz = cidx[nc];
834  NDArray re;
835  if (imag)
836  {
837  re = NDArray (dim_vector (nnz, 1));
838  data = re.fortran_vec ();
839  }
840 
841  tmp_pos = is.tellg ();
842  read_mat5_binary_data (is, data, nnz, swap,
843  static_cast<enum mat5_data_type> (type),
844  flt_fmt);
845 
846  if (! is || error_state)
847  {
848  error ("load: reading sparse matrix data for '%s'",
849  retval.c_str ());
850  goto data_read_error;
851  }
852 
853  is.seekg (tmp_pos + static_cast<std::streamoff>
854  (READ_PAD (is_small_data_element, len)));
855 
856  // imaginary data subelement
857  if (imag)
858  {
859  NDArray im (dim_vector (static_cast<int> (nnz), 1));
860 
861  if (read_mat5_tag (is, swap, type, len, is_small_data_element))
862  {
863  error ("load: reading sparse matrix data for '%s'",
864  retval.c_str ());
865  goto data_read_error;
866  }
867 
868  read_mat5_binary_data (is, im.fortran_vec (), nnz, swap,
869  static_cast<enum mat5_data_type> (type),
870  flt_fmt);
871 
872  if (! is || error_state)
873  {
874  error ("load: reading imaginary sparse matrix data for '%s'",
875  retval.c_str ());
876  goto data_read_error;
877  }
878 
879  for (octave_idx_type i = 0; i < nnz; i++)
880  scm.xdata (i) = Complex (re (i), im (i));
881 
882  tc = scm;
883  }
884  else
885  tc = sm;
886  }
887  break;
888 
890  {
891  octave_value tc2;
892  std::string nm
893  = read_mat5_binary_element (is, filename, swap, global, tc2);
894 
895  if (! is || error_state)
896  goto data_read_error;
897 
898  // Octave can handle both "/" and "\" as a directory seperator
899  // and so can ignore the separator field of m0. I think the
900  // sentinel field is also save to ignore.
903  = m0.contents ("function_handle").scalar_map_value ();
904  std::string ftype = m1.contents ("type").string_value ();
905  std::string fname = m1.contents ("function").string_value ();
906  std::string fpath = m1.contents ("file").string_value ();
907 
908  if (ftype == "simple" || ftype == "scopedfunction")
909  {
910  if (fpath.length () == 0)
911  // We have a builtin function
912  tc = make_fcn_handle (fname);
913  else
914  {
915  std::string mroot =
916  m0.contents ("matlabroot").string_value ();
917 
918  if ((fpath.length () >= mroot.length ())
919  && fpath.substr (0, mroot.length ()) == mroot
920  && OCTAVE_EXEC_PREFIX != mroot)
921  {
922  // If fpath starts with matlabroot, and matlabroot
923  // doesn't equal octave_config_info ("exec_prefix")
924  // then the function points to a version of Octave
925  // or Matlab other than the running version. In that
926  // case we replace with the same function in the
927  // running version of Octave?
928 
929  // First check if just replacing matlabroot is enough
930  std::string str = OCTAVE_EXEC_PREFIX +
931  fpath.substr (mroot.length ());
932  file_stat fs (str);
933 
934  if (fs.exists ())
935  {
936  size_t xpos
937  = str.find_last_of (file_ops::dir_sep_chars ());
938 
939  std::string dir_name = str.substr (0, xpos);
940 
941  octave_function *fcn
942  = load_fcn_from_file (str, dir_name, "", "", fname);
943 
944  if (fcn)
945  {
946  octave_value tmp (fcn);
947 
948  tc = octave_value (new octave_fcn_handle (tmp,
949  fname));
950  }
951  }
952  else
953  {
954  // Next just search for it anywhere in the system path
955  string_vector names(3);
956  names(0) = fname + ".oct";
957  names(1) = fname + ".mex";
958  names(2) = fname + ".m";
959 
961 
962  str =
964 
965  size_t xpos
966  = str.find_last_of (file_ops::dir_sep_chars ());
967 
968  std::string dir_name = str.substr (0, xpos);
969 
970  octave_function *fcn
971  = load_fcn_from_file (str, dir_name, "", "", fname);
972 
973  if (fcn)
974  {
975  octave_value tmp (fcn);
976 
977  tc = octave_value (new octave_fcn_handle (tmp,
978  fname));
979  }
980  else
981  {
982  warning ("load: can't find the file %s",
983  fpath.c_str ());
984  goto skip_ahead;
985  }
986  }
987  }
988  else
989  {
990  size_t xpos
991  = fpath.find_last_of (file_ops::dir_sep_chars ());
992 
993  std::string dir_name = fpath.substr (0, xpos);
994 
995  octave_function *fcn
996  = load_fcn_from_file (fpath, dir_name, "", "", fname);
997 
998  if (fcn)
999  {
1000  octave_value tmp (fcn);
1001 
1002  tc = octave_value (new octave_fcn_handle (tmp, fname));
1003  }
1004  else
1005  {
1006  warning ("load: can't find the file %s",
1007  fpath.c_str ());
1008  goto skip_ahead;
1009  }
1010  }
1011  }
1012  }
1013  else if (ftype == "nested")
1014  {
1015  warning ("load: can't load nested function");
1016  goto skip_ahead;
1017  }
1018  else if (ftype == "anonymous")
1019  {
1021  = m1.contents ("workspace").scalar_map_value ();
1022  uint32NDArray MCOS = m2.contents ("MCOS").uint32_array_value ();
1023  octave_idx_type off
1024  = static_cast<octave_idx_type>(MCOS(4).double_value ());
1025  m2 = subsys_ov.scalar_map_value ();
1026  m2 = m2.contents ("MCOS").scalar_map_value ();
1027  tc2 = m2.contents ("MCOS").cell_value ()(1 + off).cell_value ()(1);
1028  m2 = tc2.scalar_map_value ();
1029 
1030  unwind_protect_safe frame;
1031 
1032  // Set up temporary scope to use for evaluating the text
1033  // that defines the anonymous function.
1034 
1036  frame.add_fcn (symbol_table::erase_scope, local_scope);
1037 
1038  symbol_table::set_scope (local_scope);
1039 
1040  octave_call_stack::push (local_scope, 0);
1042 
1043  if (m2.nfields () > 0)
1044  {
1045  octave_value tmp;
1046 
1047  for (octave_map::iterator p0 = m2.begin () ;
1048  p0 != m2.end (); p0++)
1049  {
1050  std::string key = m2.key (p0);
1051  octave_value val = m2.contents (p0);
1052 
1053  symbol_table::assign (key, val, local_scope, 0);
1054  }
1055  }
1056 
1057  int parse_status;
1058  octave_value anon_fcn_handle =
1059  eval_string (fname.substr (4), true, parse_status);
1060 
1061  if (parse_status == 0)
1062  {
1063  octave_fcn_handle *fh =
1064  anon_fcn_handle.fcn_handle_value ();
1065 
1066  if (fh)
1067  tc = new octave_fcn_handle (fh->fcn_val (), "@<anonymous>");
1068  else
1069  {
1070  error ("load: failed to load anonymous function handle");
1071  goto skip_ahead;
1072  }
1073  }
1074  else
1075  {
1076  error ("load: failed to load anonymous function handle");
1077  goto skip_ahead;
1078  }
1079 
1080  frame.run ();
1081  }
1082  else
1083  {
1084  error ("load: invalid function handle type");
1085  goto skip_ahead;
1086  }
1087  }
1088  break;
1089 
1091  {
1092  octave_map m (dim_vector (1, 1));
1093  int n_fields = 2;
1094  string_vector field (n_fields);
1095 
1096  for (int i = 0; i < n_fields; i++)
1097  {
1098  int32_t fn_type;
1099  int32_t fn_len;
1100  if (read_mat5_tag (is, swap, fn_type, fn_len, is_small_data_element)
1101  || ! INT8(fn_type))
1102  {
1103  error ("load: invalid field name subelement");
1104  goto data_read_error;
1105  }
1106 
1107  OCTAVE_LOCAL_BUFFER (char, elname, fn_len + 1);
1108 
1109  std::streampos tmp_pos = is.tellg ();
1110 
1111  if (fn_len)
1112  {
1113  if (! is.read (elname, fn_len))
1114  goto data_read_error;
1115 
1116  is.seekg (tmp_pos + static_cast<std::streamoff>
1117  (READ_PAD (is_small_data_element, fn_len)));
1118  }
1119 
1120  elname[fn_len] = '\0';
1121 
1122  field(i) = elname;
1123  }
1124 
1125  std::vector<Cell> elt (n_fields);
1126 
1127  for (octave_idx_type i = 0; i < n_fields; i++)
1128  elt[i] = Cell (dims);
1129 
1130  octave_idx_type n = dims.numel ();
1131 
1132  // fields subelements
1133  for (octave_idx_type j = 0; j < n; j++)
1134  {
1135  for (octave_idx_type i = 0; i < n_fields; i++)
1136  {
1137  if (field(i) == "MCOS")
1138  {
1139  octave_value fieldtc;
1140  read_mat5_binary_element (is, filename, swap, global,
1141  fieldtc);
1142  if (! is || error_state)
1143  goto data_read_error;
1144 
1145  elt[i](j) = fieldtc;
1146  }
1147  else
1148  elt[i](j) = octave_value ();
1149  }
1150  }
1151 
1152  for (octave_idx_type i = 0; i < n_fields; i++)
1153  m.assign (field (i), elt[i]);
1154  tc = m;
1155  }
1156  break;
1157 
1158  case MAT_FILE_OBJECT_CLASS:
1159  {
1160  isclass = true;
1161 
1162  if (read_mat5_tag (is, swap, type, len, is_small_data_element)
1163  || ! INT8(type))
1164  {
1165  error ("load: invalid class name");
1166  goto skip_ahead;
1167  }
1168 
1169  {
1170  OCTAVE_LOCAL_BUFFER (char, name, len+1);
1171 
1172  std::streampos tmp_pos = is.tellg ();
1173 
1174  if (len)
1175  {
1176  if (! is.read (name, len))
1177  goto data_read_error;
1178 
1179  is.seekg (tmp_pos + static_cast<std::streamoff>
1180  (READ_PAD (is_small_data_element, len)));
1181  }
1182 
1183  name[len] = '\0';
1184  classname = name;
1185  }
1186  }
1187  // Fall-through
1188  case MAT_FILE_STRUCT_CLASS:
1189  {
1190  octave_map m (dims);
1191  int32_t fn_type;
1192  int32_t fn_len;
1193  int32_t field_name_length;
1194 
1195  // field name length subelement -- actually the maximum length
1196  // of a field name. The Matlab docs promise this will always
1197  // be 32. We read and use the actual value, on the theory
1198  // that eventually someone will recognize that's a waste of space.
1199  if (read_mat5_tag (is, swap, fn_type, fn_len, is_small_data_element)
1200  || fn_type != miINT32)
1201  {
1202  error ("load: invalid field name length subelement");
1203  goto data_read_error;
1204  }
1205 
1206  if (! is.read (reinterpret_cast<char *> (&field_name_length), fn_len))
1207  goto data_read_error;
1208 
1209  if (swap)
1210  swap_bytes<4> (&field_name_length);
1211 
1212  // field name subelement. The length of this subelement tells
1213  // us how many fields there are.
1214  if (read_mat5_tag (is, swap, fn_type, fn_len, is_small_data_element)
1215  || ! INT8(fn_type))
1216  {
1217  error ("load: invalid field name subelement");
1218  goto data_read_error;
1219  }
1220 
1221  octave_idx_type n_fields = fn_len/field_name_length;
1222 
1223  if (n_fields > 0)
1224  {
1225  fn_len = READ_PAD (is_small_data_element, fn_len);
1226 
1227  OCTAVE_LOCAL_BUFFER (char, elname, fn_len);
1228 
1229  if (! is.read (elname, fn_len))
1230  goto data_read_error;
1231 
1232  std::vector<Cell> elt (n_fields);
1233 
1234  for (octave_idx_type i = 0; i < n_fields; i++)
1235  elt[i] = Cell (dims);
1236 
1237  octave_idx_type n = dims.numel ();
1238 
1239  // fields subelements
1240  for (octave_idx_type j = 0; j < n; j++)
1241  {
1242  for (octave_idx_type i = 0; i < n_fields; i++)
1243  {
1244  octave_value fieldtc;
1245  read_mat5_binary_element (is, filename, swap, global,
1246  fieldtc);
1247  elt[i](j) = fieldtc;
1248  }
1249  }
1250 
1251  for (octave_idx_type i = 0; i < n_fields; i++)
1252  {
1253  const char *key = elname + i*field_name_length;
1254 
1255  m.assign (key, elt[i]);
1256  }
1257  }
1258 
1259  if (isclass)
1260  {
1261  if (classname == "inline")
1262  {
1263  // inline is not an object in Octave but rather an
1264  // overload of a function handle. Special case.
1265  tc =
1266  new octave_fcn_inline (m.contents ("expr")(0).string_value (),
1267  m.contents ("args")(0).string_value ());
1268  }
1269  else
1270  {
1271  octave_class* cls
1272  = new octave_class (m, classname,
1273  std::list<std::string> ());
1274 
1275  if (cls->reconstruct_exemplar ())
1276  {
1277 
1278  if (! cls->reconstruct_parents ())
1279  warning ("load: unable to reconstruct object inheritance");
1280 
1281  tc = cls;
1282  if (load_path::find_method (classname, "loadobj")
1283  != std::string ())
1284  {
1285  octave_value_list tmp = feval ("loadobj", tc, 1);
1286 
1287  if (! error_state)
1288  tc = tmp(0);
1289  else
1290  goto data_read_error;
1291  }
1292  }
1293  else
1294  {
1295  tc = m;
1296  warning ("load: element has been converted to a structure");
1297  }
1298  }
1299  }
1300  else
1301  tc = m;
1302  }
1303  break;
1304 
1305  case MAT_FILE_INT8_CLASS:
1307  break;
1308 
1309  case MAT_FILE_UINT8_CLASS:
1310  {
1312 
1313  // Logical variables can either be MAT_FILE_UINT8_CLASS or
1314  // MAT_FILE_DOUBLE_CLASS, so check if we have a logical
1315  // variable and convert it.
1316 
1317  if (logicalvar)
1318  {
1319  uint8NDArray in = tc.uint8_array_value ();
1320  octave_idx_type nel = in.numel ();
1321  boolNDArray out (dims);
1322 
1323  for (octave_idx_type i = 0; i < nel; i++)
1324  out(i) = in(i).bool_value ();
1325 
1326  tc = out;
1327  }
1328  }
1329  break;
1330 
1331  case MAT_FILE_INT16_CLASS:
1333  break;
1334 
1335  case MAT_FILE_UINT16_CLASS:
1337  break;
1338 
1339  case MAT_FILE_INT32_CLASS:
1341  break;
1342 
1343  case MAT_FILE_UINT32_CLASS:
1345  break;
1346 
1347  case MAT_FILE_INT64_CLASS:
1349  break;
1350 
1351  case MAT_FILE_UINT64_CLASS:
1353  break;
1354 
1355 
1356  case MAT_FILE_SINGLE_CLASS:
1357  {
1358  FloatNDArray re (dims);
1359 
1360  // real data subelement
1361 
1362  std::streampos tmp_pos;
1363 
1364  if (read_mat5_tag (is, swap, type, len, is_small_data_element))
1365  {
1366  error ("load: reading matrix data for '%s'", retval.c_str ());
1367  goto data_read_error;
1368  }
1369 
1370  octave_idx_type n = re.numel ();
1371  tmp_pos = is.tellg ();
1372  read_mat5_binary_data (is, re.fortran_vec (), n, swap,
1373  static_cast<enum mat5_data_type> (type),
1374  flt_fmt);
1375 
1376  if (! is || error_state)
1377  {
1378  error ("load: reading matrix data for '%s'", retval.c_str ());
1379  goto data_read_error;
1380  }
1381 
1382  is.seekg (tmp_pos + static_cast<std::streamoff>
1383  (READ_PAD (is_small_data_element, len)));
1384 
1385  if (imag)
1386  {
1387  // imaginary data subelement
1388 
1389  FloatNDArray im (dims);
1390 
1391  if (read_mat5_tag (is, swap, type, len, is_small_data_element))
1392  {
1393  error ("load: reading matrix data for '%s'", retval.c_str ());
1394  goto data_read_error;
1395  }
1396 
1397  n = im.numel ();
1398  read_mat5_binary_data (is, im.fortran_vec (), n, swap,
1399  static_cast<enum mat5_data_type> (type),
1400  flt_fmt);
1401 
1402  if (! is || error_state)
1403  {
1404  error ("load: reading imaginary matrix data for '%s'",
1405  retval.c_str ());
1406  goto data_read_error;
1407  }
1408 
1409  FloatComplexNDArray ctmp (dims);
1410 
1411  for (octave_idx_type i = 0; i < n; i++)
1412  ctmp(i) = FloatComplex (re(i), im(i));
1413 
1414  tc = ctmp;
1415  }
1416  else
1417  tc = re;
1418  }
1419  break;
1420 
1421  case MAT_FILE_CHAR_CLASS:
1422  // handle as a numerical array to start with
1423 
1424  case MAT_FILE_DOUBLE_CLASS:
1425  default:
1426  {
1427  NDArray re (dims);
1428 
1429  // real data subelement
1430 
1431  std::streampos tmp_pos;
1432 
1433  if (read_mat5_tag (is, swap, type, len, is_small_data_element))
1434  {
1435  error ("load: reading matrix data for '%s'", retval.c_str ());
1436  goto data_read_error;
1437  }
1438 
1439  octave_idx_type n = re.numel ();
1440  tmp_pos = is.tellg ();
1441  read_mat5_binary_data (is, re.fortran_vec (), n, swap,
1442  static_cast<enum mat5_data_type> (type),
1443  flt_fmt);
1444 
1445  if (! is || error_state)
1446  {
1447  error ("load: reading matrix data for '%s'", retval.c_str ());
1448  goto data_read_error;
1449  }
1450 
1451  is.seekg (tmp_pos + static_cast<std::streamoff>
1452  (READ_PAD (is_small_data_element, len)));
1453 
1454  if (logicalvar)
1455  {
1456  // Logical variables can either be MAT_FILE_UINT8_CLASS or
1457  // MAT_FILE_DOUBLE_CLASS, so check if we have a logical
1458  // variable and convert it.
1459 
1460  boolNDArray out (dims);
1461 
1462  for (octave_idx_type i = 0; i < n; i++)
1463  out (i) = static_cast<bool> (re (i));
1464 
1465  tc = out;
1466  }
1467  else if (imag)
1468  {
1469  // imaginary data subelement
1470 
1471  NDArray im (dims);
1472 
1473  if (read_mat5_tag (is, swap, type, len, is_small_data_element))
1474  {
1475  error ("load: reading matrix data for '%s'", retval.c_str ());
1476  goto data_read_error;
1477  }
1478 
1479  n = im.numel ();
1480  read_mat5_binary_data (is, im.fortran_vec (), n, swap,
1481  static_cast<enum mat5_data_type> (type),
1482  flt_fmt);
1483 
1484  if (! is || error_state)
1485  {
1486  error ("load: reading imaginary matrix data for '%s'",
1487  retval.c_str ());
1488  goto data_read_error;
1489  }
1490 
1491  ComplexNDArray ctmp (dims);
1492 
1493  for (octave_idx_type i = 0; i < n; i++)
1494  ctmp(i) = Complex (re(i), im(i));
1495 
1496  tc = ctmp;
1497  }
1498  else
1499  {
1500  if (arrayclass == MAT_FILE_CHAR_CLASS)
1501  {
1502  if (type == miUTF16 || type == miUTF32)
1503  {
1504  bool found_big_char = false;
1505  for (octave_idx_type i = 0; i < n; i++)
1506  {
1507  if (re(i) > 127)
1508  {
1509  re(i) = '?';
1510  found_big_char = true;
1511  }
1512  }
1513 
1514  if (found_big_char)
1515  warning ("load: can not read non-ASCII portions of UTF characters; replacing unreadable characters with '?'");
1516  }
1517  else if (type == miUTF8)
1518  {
1519  // Search for multi-byte encoded UTF8 characters and
1520  // replace with 0x3F for '?'... Give the user a warning
1521 
1522  bool utf8_multi_byte = false;
1523  for (octave_idx_type i = 0; i < n; i++)
1524  {
1525  unsigned char a = static_cast<unsigned char> (re(i));
1526  if (a > 0x7f)
1527  utf8_multi_byte = true;
1528  }
1529 
1530  if (utf8_multi_byte)
1531  {
1532  warning ("load: can not read multi-byte encoded UTF8 characters; replacing unreadable characters with '?'");
1533  for (octave_idx_type i = 0; i < n; i++)
1534  {
1535  unsigned char a
1536  = static_cast<unsigned char> (re(i));
1537  if (a > 0x7f)
1538  re(i) = '?';
1539  }
1540  }
1541  }
1542  tc = re;
1543  tc = tc.convert_to_str (false, true, '\'');
1544  }
1545  else
1546  tc = re;
1547  }
1548  }
1549  }
1550 
1551  is.seekg (pos + static_cast<std::streamoff> (element_length));
1552 
1553  if (is.eof ())
1554  is.clear ();
1555 
1556  return retval;
1557 
1558 data_read_error:
1559 early_read_error:
1560  error ("load: trouble reading binary file '%s'", filename.c_str ());
1561  return std::string ();
1562 
1563 skip_ahead:
1564  warning ("skipping over '%s'", retval.c_str ());
1565  is.seekg (pos + static_cast<std::streamoff> (element_length));
1566  return read_mat5_binary_element (is, filename, swap, global, tc);
1567 }
1568 
1569 int
1570 read_mat5_binary_file_header (std::istream& is, bool& swap, bool quiet,
1571  const std::string& filename)
1572 {
1573  int16_t version = 0;
1574  int16_t magic = 0;
1575  uint64_t subsys_offset;
1576 
1577  is.seekg (116, std::ios::beg);
1578  is.read (reinterpret_cast<char *> (&subsys_offset), 8);
1579 
1580  is.seekg (124, std::ios::beg);
1581  is.read (reinterpret_cast<char *> (&version), 2);
1582  is.read (reinterpret_cast<char *> (&magic), 2);
1583 
1584  if (magic == 0x4d49)
1585  swap = 0;
1586  else if (magic == 0x494d)
1587  swap = 1;
1588  else
1589  {
1590  if (! quiet)
1591  error ("load: can't read binary file");
1592  return -1;
1593  }
1594 
1595  if (! swap) // version number is inverse swapped!
1596  version = ((version >> 8) & 0xff) + ((version & 0xff) << 8);
1597 
1598  if (version != 1 && !quiet)
1599  warning ("load: found version %d binary MAT file, "
1600  "but only prepared for version 1", version);
1601 
1602  if (swap)
1603  swap_bytes<8> (&subsys_offset, 1);
1604 
1605  if (subsys_offset != 0x2020202020202020ULL && subsys_offset != 0ULL)
1606  {
1607  // Read the subsystem data block
1608  is.seekg (subsys_offset, std::ios::beg);
1609 
1610  octave_value tc;
1611  bool global;
1612  read_mat5_binary_element (is, filename, swap, global, tc);
1613 
1614  if (!is || error_state)
1615  return -1;
1616 
1617  if (tc.is_uint8_type ())
1618  {
1619  const uint8NDArray itmp = tc.uint8_array_value ();
1620  octave_idx_type ilen = itmp.numel ();
1621 
1622  // Why should I have to initialize outbuf as just overwrite
1623  std::string outbuf (ilen - 7, ' ');
1624 
1625  // FIXME: find a way to avoid casting away const here
1626  char *ctmp = const_cast<char *> (outbuf.c_str ());
1627  for (octave_idx_type j = 8; j < ilen; j++)
1628  ctmp[j-8] = itmp(j).char_value ();
1629 
1630  std::istringstream fh_ws (outbuf);
1631 
1632  read_mat5_binary_element (fh_ws, filename, swap, global, subsys_ov);
1633 
1634  if (error_state)
1635  return -1;
1636  }
1637  else
1638  return -1;
1639 
1640  // Reposition to just after the header
1641  is.seekg (128, std::ios::beg);
1642  }
1643 
1644  return 0;
1645 }
1646 
1647 static int
1648 write_mat5_tag (std::ostream& is, int type, octave_idx_type bytes)
1649 {
1650  int32_t temp;
1651 
1652  if (bytes > 0 && bytes <= 4)
1653  temp = (bytes << 16) + type;
1654  else
1655  {
1656  temp = type;
1657  if (! is.write (reinterpret_cast<char *> (&temp), 4))
1658  goto data_write_error;
1659  temp = bytes;
1660  }
1661 
1662  if (! is.write (reinterpret_cast<char *> (&temp), 4))
1663  goto data_write_error;
1664 
1665  return 0;
1666 
1667 data_write_error:
1668  return 1;
1669 }
1670 
1671 // Have to use copy here to avoid writing over data accessed via
1672 // Matrix::data().
1673 
1674 #define MAT5_DO_WRITE(TYPE, data, count, stream) \
1675  do \
1676  { \
1677  OCTAVE_LOCAL_BUFFER (TYPE, ptr, count); \
1678  for (octave_idx_type i = 0; i < count; i++) \
1679  ptr[i] = static_cast<TYPE> (data[i]); \
1680  std::streamsize n_bytes = sizeof (TYPE) * static_cast<std::streamsize> (count); \
1681  stream.write (reinterpret_cast<char *> (ptr), n_bytes); \
1682  } \
1683  while (0)
1684 
1685 // write out the numeric values in M to OS,
1686 // preceded by the appropriate tag.
1687 static void
1688 write_mat5_array (std::ostream& os, const NDArray& m, bool save_as_floats)
1689 {
1690  save_type st = LS_DOUBLE;
1691  const double *data = m.data ();
1692 
1693  if (save_as_floats)
1694  {
1695  if (m.too_large_for_float ())
1696  {
1697  warning ("save: some values too large to save as floats --");
1698  warning ("save: saving as doubles instead");
1699  }
1700  else
1701  st = LS_FLOAT;
1702  }
1703 
1704  double max_val, min_val;
1705  if (m.all_integers (max_val, min_val))
1706  st = get_save_type (max_val, min_val);
1707 
1708  mat5_data_type mst;
1709  int size;
1710  switch (st)
1711  {
1712  default:
1713  case LS_DOUBLE: mst = miDOUBLE; size = 8; break;
1714  case LS_FLOAT: mst = miSINGLE; size = 4; break;
1715  case LS_U_CHAR: mst = miUINT8; size = 1; break;
1716  case LS_U_SHORT: mst = miUINT16; size = 2; break;
1717  case LS_U_INT: mst = miUINT32; size = 4; break;
1718  case LS_CHAR: mst = miINT8; size = 1; break;
1719  case LS_SHORT: mst = miINT16; size = 2; break;
1720  case LS_INT: mst = miINT32; size = 4; break;
1721  }
1722 
1723  octave_idx_type nel = m.numel ();
1724  octave_idx_type len = nel*size;
1725 
1726  write_mat5_tag (os, mst, len);
1727 
1728  {
1729  switch (st)
1730  {
1731  case LS_U_CHAR:
1732  MAT5_DO_WRITE (uint8_t, data, nel, os);
1733  break;
1734 
1735  case LS_U_SHORT:
1736  MAT5_DO_WRITE (uint16_t, data, nel, os);
1737  break;
1738 
1739  case LS_U_INT:
1740  MAT5_DO_WRITE (uint32_t, data, nel, os);
1741  break;
1742 
1743  case LS_U_LONG:
1744  MAT5_DO_WRITE (uint64_t, data, nel, os);
1745  break;
1746 
1747  case LS_CHAR:
1748  MAT5_DO_WRITE (int8_t, data, nel, os);
1749  break;
1750 
1751  case LS_SHORT:
1752  MAT5_DO_WRITE (int16_t, data, nel, os);
1753  break;
1754 
1755  case LS_INT:
1756  MAT5_DO_WRITE (int32_t, data, nel, os);
1757  break;
1758 
1759  case LS_LONG:
1760  MAT5_DO_WRITE (int64_t, data, nel, os);
1761  break;
1762 
1763  case LS_FLOAT:
1764  MAT5_DO_WRITE (float, data, nel, os);
1765  break;
1766 
1767  case LS_DOUBLE: // No conversion necessary.
1768  os.write (reinterpret_cast<const char *> (data), len);
1769  break;
1770 
1771  default:
1772  (*current_liboctave_error_handler)
1773  ("unrecognized data format requested");
1774  break;
1775  }
1776  }
1777  if (PAD (len) > len)
1778  {
1779  static char buf[9]="\x00\x00\x00\x00\x00\x00\x00\x00";
1780  os.write (buf, PAD (len) - len);
1781  }
1782 }
1783 
1784 static void
1785 write_mat5_array (std::ostream& os, const FloatNDArray& m, bool)
1786 {
1787  save_type st = LS_FLOAT;
1788  const float *data = m.data ();
1789 
1790  float max_val, min_val;
1791  if (m.all_integers (max_val, min_val))
1792  st = get_save_type (max_val, min_val);
1793 
1794  mat5_data_type mst;
1795  int size;
1796  switch (st)
1797  {
1798  default:
1799  case LS_DOUBLE: mst = miDOUBLE; size = 8; break;
1800  case LS_FLOAT: mst = miSINGLE; size = 4; break;
1801  case LS_U_CHAR: mst = miUINT8; size = 1; break;
1802  case LS_U_SHORT: mst = miUINT16; size = 2; break;
1803  case LS_U_INT: mst = miUINT32; size = 4; break;
1804  case LS_CHAR: mst = miINT8; size = 1; break;
1805  case LS_SHORT: mst = miINT16; size = 2; break;
1806  case LS_INT: mst = miINT32; size = 4; break;
1807  }
1808 
1809  octave_idx_type nel = m.numel ();
1810  octave_idx_type len = nel*size;
1811 
1812  write_mat5_tag (os, mst, len);
1813 
1814  {
1815  switch (st)
1816  {
1817  case LS_U_CHAR:
1818  MAT5_DO_WRITE (uint8_t, data, nel, os);
1819  break;
1820 
1821  case LS_U_SHORT:
1822  MAT5_DO_WRITE (uint16_t, data, nel, os);
1823  break;
1824 
1825  case LS_U_INT:
1826  MAT5_DO_WRITE (uint32_t, data, nel, os);
1827  break;
1828 
1829  case LS_U_LONG:
1830  MAT5_DO_WRITE (uint64_t, data, nel, os);
1831  break;
1832 
1833  case LS_CHAR:
1834  MAT5_DO_WRITE (int8_t, data, nel, os);
1835  break;
1836 
1837  case LS_SHORT:
1838  MAT5_DO_WRITE (int16_t, data, nel, os);
1839  break;
1840 
1841  case LS_INT:
1842  MAT5_DO_WRITE (int32_t, data, nel, os);
1843  break;
1844 
1845  case LS_LONG:
1846  MAT5_DO_WRITE (int64_t, data, nel, os);
1847  break;
1848 
1849  case LS_FLOAT: // No conversion necessary.
1850  os.write (reinterpret_cast<const char *> (data), len);
1851  break;
1852 
1853  case LS_DOUBLE:
1854  MAT5_DO_WRITE (double, data, nel, os);
1855  break;
1856 
1857  default:
1858  (*current_liboctave_error_handler)
1859  ("unrecognized data format requested");
1860  break;
1861  }
1862  }
1863  if (PAD (len) > len)
1864  {
1865  static char buf[9]="\x00\x00\x00\x00\x00\x00\x00\x00";
1866  os.write (buf, PAD (len) - len);
1867  }
1868 }
1869 
1870 template <class T>
1871 void
1872 write_mat5_integer_data (std::ostream& os, const T *m, int size,
1873  octave_idx_type nel)
1874 {
1875  mat5_data_type mst;
1876  unsigned len;
1877 
1878  switch (size)
1879  {
1880  case 1:
1881  mst = miUINT8;
1882  break;
1883  case 2:
1884  mst = miUINT16;
1885  break;
1886  case 4:
1887  mst = miUINT32;
1888  break;
1889  case 8:
1890  mst = miUINT64;
1891  break;
1892  case -1:
1893  mst = miINT8;
1894  size = - size;
1895  break;
1896  case -2:
1897  mst = miINT16;
1898  size = - size;
1899  break;
1900  case -4:
1901  mst = miINT32;
1902  size = - size;
1903  break;
1904  case -8:
1905  default:
1906  mst = miINT64;
1907  size = - size;
1908  break;
1909  }
1910 
1911  len = nel*size;
1912  write_mat5_tag (os, mst, len);
1913 
1914  os.write (reinterpret_cast<const char *> (m), len);
1915 
1916  if (PAD (len) > len)
1917  {
1918  static char buf[9]="\x00\x00\x00\x00\x00\x00\x00\x00";
1919  os.write (buf, PAD (len) - len);
1920  }
1921 }
1922 
1923 template void
1924 write_mat5_integer_data (std::ostream& os, const octave_int8 *m,
1925  int size, octave_idx_type nel);
1926 
1927 template void
1928 write_mat5_integer_data (std::ostream& os, const octave_int16 *m,
1929  int size, octave_idx_type nel);
1930 
1931 template void
1932 write_mat5_integer_data (std::ostream& os, const octave_int32 *m,
1933  int size, octave_idx_type nel);
1934 
1935 template void
1936 write_mat5_integer_data (std::ostream& os, const octave_int64 *m,
1937  int size, octave_idx_type nel);
1938 
1939 template void
1940 write_mat5_integer_data (std::ostream& os, const octave_uint8 *m,
1941  int size, octave_idx_type nel);
1942 
1943 template void
1944 write_mat5_integer_data (std::ostream& os, const octave_uint16 *m,
1945  int size, octave_idx_type nel);
1946 
1947 template void
1948 write_mat5_integer_data (std::ostream& os, const octave_uint32 *m,
1949  int size, octave_idx_type nel);
1950 
1951 template void
1952 write_mat5_integer_data (std::ostream& os, const octave_uint64 *m,
1953  int size, octave_idx_type nel);
1954 
1955 template void
1956 write_mat5_integer_data (std::ostream& os, const int *m,
1957  int size, octave_idx_type nel);
1958 
1959 // Write out cell element values in the cell array to OS, preceded by
1960 // the appropriate tag.
1961 
1962 static bool
1963 write_mat5_cell_array (std::ostream& os, const Cell& cell,
1964  bool mark_as_global, bool save_as_floats)
1965 {
1966  octave_idx_type nel = cell.numel ();
1967 
1968  for (octave_idx_type i = 0; i < nel; i++)
1969  {
1970  octave_value ov = cell(i);
1971 
1972  if (! save_mat5_binary_element (os, ov, "", mark_as_global,
1973  false, save_as_floats))
1974  return false;
1975  }
1976 
1977  return true;
1978 }
1979 
1980 int
1981 save_mat5_array_length (const double* val, octave_idx_type nel,
1982  bool save_as_floats)
1983 {
1984  if (nel > 0)
1985  {
1986  int size = 8;
1987 
1988  if (save_as_floats)
1989  {
1990  bool too_large_for_float = false;
1991  for (octave_idx_type i = 0; i < nel; i++)
1992  {
1993  double tmp = val[i];
1994 
1995  if (xfinite (tmp)
1996  && fabs (tmp) > std::numeric_limits<float>::max ())
1997  {
1998  too_large_for_float = true;
1999  break;
2000  }
2001  }
2002 
2003  if (!too_large_for_float)
2004  size = 4;
2005  }
2006 
2007  // The code below is disabled since get_save_type currently doesn't
2008  // deal with integer types. This will need to be activated if
2009  // get_save_type is changed.
2010 
2011  // double max_val = val[0];
2012  // double min_val = val[0];
2013  // bool all_integers = true;
2014  //
2015  // for (int i = 0; i < nel; i++)
2016  // {
2017  // double val = val[i];
2018  //
2019  // if (val > max_val)
2020  // max_val = val;
2021  //
2022  // if (val < min_val)
2023  // min_val = val;
2024  //
2025  // if (D_NINT (val) != val)
2026  // {
2027  // all_integers = false;
2028  // break;
2029  // }
2030  // }
2031  //
2032  // if (all_integers)
2033  // {
2034  // if (max_val < 256 && min_val > -1)
2035  // size = 1;
2036  // else if (max_val < 65536 && min_val > -1)
2037  // size = 2;
2038  // else if (max_val < 4294967295UL && min_val > -1)
2039  // size = 4;
2040  // else if (max_val < 128 && min_val >= -128)
2041  // size = 1;
2042  // else if (max_val < 32768 && min_val >= -32768)
2043  // size = 2;
2044  // else if (max_val <= 2147483647L && min_val >= -2147483647L)
2045  // size = 4;
2046  // }
2047 
2048  return 8 + nel * size;
2049  }
2050  else
2051  return 8;
2052 }
2053 
2054 int
2055 save_mat5_array_length (const float* /* val */, octave_idx_type nel, bool)
2056 {
2057  if (nel > 0)
2058  {
2059  int size = 4;
2060 
2061 
2062  // The code below is disabled since get_save_type currently doesn't
2063  // deal with integer types. This will need to be activated if
2064  // get_save_type is changed.
2065 
2066  // float max_val = val[0];
2067  // float min_val = val[0];
2068  // bool all_integers = true;
2069  //
2070  // for (int i = 0; i < nel; i++)
2071  // {
2072  // float val = val[i];
2073  //
2074  // if (val > max_val)
2075  // max_val = val;
2076  //
2077  // if (val < min_val)
2078  // min_val = val;
2079  //
2080  // if (D_NINT (val) != val)
2081  // {
2082  // all_integers = false;
2083  // break;
2084  // }
2085  // }
2086  //
2087  // if (all_integers)
2088  // {
2089  // if (max_val < 256 && min_val > -1)
2090  // size = 1;
2091  // else if (max_val < 65536 && min_val > -1)
2092  // size = 2;
2093  // else if (max_val < 4294967295UL && min_val > -1)
2094  // size = 4;
2095  // else if (max_val < 128 && min_val >= -128)
2096  // size = 1;
2097  // else if (max_val < 32768 && min_val >= -32768)
2098  // size = 2;
2099  // else if (max_val <= 2147483647L && min_val >= -2147483647L)
2100  // size = 4;
2101  // }
2102 
2103  // Round nel up to nearest even number of elements. Take into account
2104  // Short tags for 4 byte elements.
2105  return PAD ((nel > 0 && nel * size <= 4 ? 4 : 8) + nel * size);
2106  }
2107  else
2108  return 8;
2109 }
2110 
2111 int
2113  bool save_as_floats)
2114 {
2115  int ret;
2116 
2117  OCTAVE_LOCAL_BUFFER (double, tmp, nel);
2118 
2119  for (octave_idx_type i = 1; i < nel; i++)
2120  tmp[i] = std::real (val[i]);
2121 
2122  ret = save_mat5_array_length (tmp, nel, save_as_floats);
2123 
2124  for (octave_idx_type i = 1; i < nel; i++)
2125  tmp[i] = std::imag (val[i]);
2126 
2127  ret += save_mat5_array_length (tmp, nel, save_as_floats);
2128 
2129  return ret;
2130 }
2131 
2132 int
2134  bool save_as_floats)
2135 {
2136  int ret;
2137 
2138  OCTAVE_LOCAL_BUFFER (float, tmp, nel);
2139 
2140  for (octave_idx_type i = 1; i < nel; i++)
2141  tmp[i] = std::real (val[i]);
2142 
2143  ret = save_mat5_array_length (tmp, nel, save_as_floats);
2144 
2145  for (octave_idx_type i = 1; i < nel; i++)
2146  tmp[i] = std::imag (val[i]);
2147 
2148  ret += save_mat5_array_length (tmp, nel, save_as_floats);
2149 
2150  return ret;
2151 }
2152 
2153 int
2154 save_mat5_element_length (const octave_value& tc, const std::string& name,
2155  bool save_as_floats, bool mat7_format)
2156 {
2157  size_t max_namelen = 63;
2158  size_t len = name.length ();
2159  std::string cname = tc.class_name ();
2160  int ret = 32;
2161 
2162  if (len > 4)
2163  ret += PAD (len > max_namelen ? max_namelen : len);
2164 
2165  ret += PAD (4 * tc.ndims ());
2166 
2167  if (tc.is_string ())
2168  {
2169  charNDArray chm = tc.char_array_value ();
2170  ret += 8;
2171  if (chm.numel () > 2)
2172  ret += PAD (2 * chm.numel ());
2173  }
2174  else if (tc.is_sparse_type ())
2175  {
2176  if (tc.is_complex_type ())
2177  {
2179  octave_idx_type nc = m.cols ();
2180  octave_idx_type nnz = m.nnz ();
2181 
2182  ret += 16 + save_mat5_array_length (m.data (), nnz, save_as_floats);
2183  if (nnz > 1)
2184  ret += PAD (nnz * sizeof (int32_t));
2185  if (nc > 0)
2186  ret += PAD ((nc + 1) * sizeof (int32_t));
2187  }
2188  else
2189  {
2190  const SparseMatrix m = tc.sparse_matrix_value ();
2191  octave_idx_type nc = m.cols ();
2192  octave_idx_type nnz = m.nnz ();
2193 
2194  ret += 16 + save_mat5_array_length (m.data (), nnz, save_as_floats);
2195  if (nnz > 1)
2196  ret += PAD (nnz * sizeof (int32_t));
2197  if (nc > 0)
2198  ret += PAD ((nc + 1) * sizeof (int32_t));
2199  }
2200  }
2201 
2202 #define INT_LEN(nel, size) \
2203  { \
2204  ret += 8; \
2205  octave_idx_type sz = nel * size; \
2206  if (sz > 4) \
2207  ret += PAD (sz); \
2208  }
2209 
2210  else if (cname == "int8")
2211  INT_LEN (tc.int8_array_value ().numel (), 1)
2212  else if (cname == "int16")
2213  INT_LEN (tc.int16_array_value ().numel (), 2)
2214  else if (cname == "int32")
2215  INT_LEN (tc.int32_array_value ().numel (), 4)
2216  else if (cname == "int64")
2217  INT_LEN (tc.int64_array_value ().numel (), 8)
2218  else if (cname == "uint8")
2219  INT_LEN (tc.uint8_array_value ().numel (), 1)
2220  else if (cname == "uint16")
2221  INT_LEN (tc.uint16_array_value ().numel (), 2)
2222  else if (cname == "uint32")
2223  INT_LEN (tc.uint32_array_value ().numel (), 4)
2224  else if (cname == "uint64")
2225  INT_LEN (tc.uint64_array_value ().numel (), 8)
2226  else if (tc.is_bool_type ())
2227  INT_LEN (tc.bool_array_value ().numel (), 1)
2228  else if (tc.is_real_scalar () || tc.is_real_matrix () || tc.is_range ())
2229  {
2230  if (tc.is_single_type ())
2231  {
2232  const FloatNDArray m = tc.float_array_value ();
2233  ret += save_mat5_array_length (m.fortran_vec (), m.numel (),
2234  save_as_floats);
2235  }
2236  else
2237  {
2238  const NDArray m = tc.array_value ();
2239  ret += save_mat5_array_length (m.fortran_vec (), m.numel (),
2240  save_as_floats);
2241  }
2242  }
2243  else if (tc.is_cell ())
2244  {
2245  Cell cell = tc.cell_value ();
2246  octave_idx_type nel = cell.numel ();
2247 
2248  for (int i = 0; i < nel; i++)
2249  ret += 8 +
2250  save_mat5_element_length (cell (i), "", save_as_floats, mat7_format);
2251  }
2252  else if (tc.is_complex_scalar () || tc.is_complex_matrix ())
2253  {
2254  if (tc.is_single_type ())
2255  {
2257  ret += save_mat5_array_length (m.fortran_vec (), m.numel (),
2258  save_as_floats);
2259  }
2260  else
2261  {
2262  const ComplexNDArray m = tc.complex_array_value ();
2263  ret += save_mat5_array_length (m.fortran_vec (), m.numel (),
2264  save_as_floats);
2265  }
2266  }
2267  else if (tc.is_map () || tc.is_inline_function () || tc.is_object ())
2268  {
2269  int fieldcnt = 0;
2270  const octave_map m = tc.map_value ();
2271  octave_idx_type nel = m.numel ();
2272 
2273  if (tc.is_inline_function ())
2274  // length of "inline" is 6
2275  ret += 8 + PAD (6 > max_namelen ? max_namelen : 6);
2276  else if (tc.is_object ())
2277  {
2278  size_t classlen = tc.class_name (). length ();
2279 
2280  ret += 8 + PAD (classlen > max_namelen ? max_namelen : classlen);
2281  }
2282 
2283  for (octave_map::const_iterator i = m.begin (); i != m.end (); i++)
2284  fieldcnt++;
2285 
2286  ret += 16 + fieldcnt * (max_namelen + 1);
2287 
2288 
2289  for (octave_idx_type j = 0; j < nel; j++)
2290  {
2291 
2292  for (octave_map::const_iterator i = m.begin (); i != m.end (); i++)
2293  {
2294  const Cell elts = m.contents (i);
2295 
2296  ret += 8 + save_mat5_element_length (elts(j), "",
2297  save_as_floats, mat7_format);
2298  }
2299  }
2300  }
2301  else
2302  ret = -1;
2303 
2304  return ret;
2305 }
2306 
2307 static void
2309  const octave_idx_type *idx,
2310  octave_idx_type nel)
2311 {
2312  int tmp = sizeof (int32_t);
2313 
2314  OCTAVE_LOCAL_BUFFER (int32_t, tmp_idx, nel);
2315 
2316  for (octave_idx_type i = 0; i < nel; i++)
2317  tmp_idx[i] = idx[i];
2318 
2319  write_mat5_integer_data (os, tmp_idx, -tmp, nel);
2320 }
2321 
2322 static void
2323 gripe_dim_too_large (const std::string& name)
2324 {
2325  warning ("save: skipping %s: dimension too large for MAT format",
2326  name.c_str ());
2327 }
2328 
2329 // save the data from TC along with the corresponding NAME on stream
2330 // OS in the MatLab version 5 binary format. Return true on success.
2331 
2332 bool
2333 save_mat5_binary_element (std::ostream& os,
2334  const octave_value& tc, const std::string& name,
2335  bool mark_as_global, bool mat7_format,
2336  bool save_as_floats, bool compressing)
2337 {
2338  int32_t flags = 0;
2339  int32_t nnz_32 = 0;
2340  std::string cname = tc.class_name ();
2341  size_t max_namelen = 63;
2342 
2343  dim_vector dv = tc.dims ();
2344  int nd = tc.ndims ();
2345  int dim_len = 4*nd;
2346 
2347  static octave_idx_type max_dim_val = std::numeric_limits<int32_t>::max ();
2348 
2349  for (int i = 0; i < nd; i++)
2350  {
2351  if (dv(i) > max_dim_val)
2352  {
2353  gripe_dim_too_large (name);
2354  goto skip_to_next;
2355  }
2356  }
2357 
2358  if (tc.is_sparse_type ())
2359  {
2360  octave_idx_type nnz;
2361  octave_idx_type nc;
2362 
2363  if (tc.is_complex_type ())
2364  {
2366  nnz = scm.nzmax ();
2367  nc = scm.cols ();
2368  }
2369  else
2370  {
2371  SparseMatrix sm = tc.sparse_matrix_value ();
2372  nnz = sm.nzmax ();
2373  nc = sm.cols ();
2374  }
2375 
2376  if (nnz > max_dim_val || nc + 1 > max_dim_val)
2377  {
2378  gripe_dim_too_large (name);
2379  goto skip_to_next;
2380  }
2381 
2382  nnz_32 = nnz;
2383  }
2384  else if (dv.numel () > max_dim_val)
2385  {
2386  gripe_dim_too_large (name);
2387  goto skip_to_next;
2388  }
2389 
2390 #ifdef HAVE_ZLIB
2391  if (mat7_format && !compressing)
2392  {
2393  bool ret = false;
2394 
2395  std::ostringstream buf;
2396 
2397  // The code seeks backwards in the stream to fix the header. Can't
2398  // do this with zlib, so use a stringstream.
2399  ret = save_mat5_binary_element (buf, tc, name, mark_as_global, true,
2400  save_as_floats, true);
2401 
2402  if (ret)
2403  {
2404  // destLen must be at least 0.1% larger than source buffer
2405  // + 12 bytes. Reality is it must be larger again than that.
2406  std::string buf_str = buf.str ();
2407  uLongf srcLen = buf_str.length ();
2408  uLongf destLen = srcLen * 101 / 100 + 12;
2409  OCTAVE_LOCAL_BUFFER (char, out_buf, destLen);
2410 
2411  if (compress (reinterpret_cast<Bytef *> (out_buf), &destLen,
2412  reinterpret_cast<const Bytef *> (buf_str.c_str ()),
2413  srcLen)
2414  == Z_OK)
2415  {
2417  static_cast<octave_idx_type> (destLen));
2418 
2419  os.write (out_buf, destLen);
2420  }
2421  else
2422  {
2423  error ("save: error compressing data element");
2424  ret = false;
2425  }
2426  }
2427 
2428  return ret;
2429  }
2430 #endif
2431 
2433  (tc, name, save_as_floats, mat7_format));
2434 
2435  // array flags subelement
2436  write_mat5_tag (os, miUINT32, 8);
2437 
2438  if (tc.is_bool_type ())
2439  flags |= 0x0200;
2440 
2441  if (mark_as_global)
2442  flags |= 0x0400;
2443 
2444  if (tc.is_complex_scalar () || tc.is_complex_matrix ())
2445  flags |= 0x0800;
2446 
2447  if (tc.is_string ())
2448  flags |= MAT_FILE_CHAR_CLASS;
2449  else if (cname == "int8")
2450  flags |= MAT_FILE_INT8_CLASS;
2451  else if (cname == "int16")
2452  flags |= MAT_FILE_INT16_CLASS;
2453  else if (cname == "int32")
2454  flags |= MAT_FILE_INT32_CLASS;
2455  else if (cname == "int64")
2456  flags |= MAT_FILE_INT64_CLASS;
2457  else if (cname == "uint8" || tc.is_bool_type ())
2458  flags |= MAT_FILE_UINT8_CLASS;
2459  else if (cname == "uint16")
2460  flags |= MAT_FILE_UINT16_CLASS;
2461  else if (cname == "uint32")
2462  flags |= MAT_FILE_UINT32_CLASS;
2463  else if (cname == "uint64")
2464  flags |= MAT_FILE_UINT64_CLASS;
2465  else if (tc.is_sparse_type ())
2466  flags |= MAT_FILE_SPARSE_CLASS;
2467  else if (tc.is_real_scalar () || tc.is_real_matrix () || tc.is_range ()
2468  || tc.is_complex_scalar () || tc.is_complex_matrix ())
2469  {
2470  if (tc.is_single_type ())
2471  flags |= MAT_FILE_SINGLE_CLASS;
2472  else
2473  flags |= MAT_FILE_DOUBLE_CLASS;
2474  }
2475  else if (tc.is_map ())
2476  flags |= MAT_FILE_STRUCT_CLASS;
2477  else if (tc.is_cell ())
2478  flags |= MAT_FILE_CELL_CLASS;
2479  else if (tc.is_inline_function () || tc.is_object ())
2480  flags |= MAT_FILE_OBJECT_CLASS;
2481  else
2482  {
2483  gripe_wrong_type_arg ("save", tc, false);
2484  goto error_cleanup;
2485  }
2486 
2487  os.write (reinterpret_cast<char *> (&flags), 4);
2488  // Matlab seems to have trouble reading files that have nzmax == 0 at
2489  // this point in the file.
2490  if (nnz_32 == 0)
2491  nnz_32 = 1;
2492  os.write (reinterpret_cast<char *> (&nnz_32), 4);
2493 
2494  write_mat5_tag (os, miINT32, dim_len);
2495 
2496  for (int i = 0; i < nd; i++)
2497  {
2498  int32_t n = dv(i);
2499  os.write (reinterpret_cast<char *> (&n), 4);
2500  }
2501 
2502  if (PAD (dim_len) > dim_len)
2503  {
2504  static char buf[9]="\x00\x00\x00\x00\x00\x00\x00\x00";
2505  os.write (buf, PAD (dim_len) - dim_len);
2506  }
2507 
2508  // array name subelement
2509  {
2510  size_t namelen = name.length ();
2511 
2512  if (namelen > max_namelen)
2513  namelen = max_namelen; // Truncate names if necessary
2514 
2515  int paddedlength = PAD (namelen);
2516 
2517  write_mat5_tag (os, miINT8, namelen);
2518  OCTAVE_LOCAL_BUFFER (char, paddedname, paddedlength);
2519  memset (paddedname, 0, paddedlength);
2520  strncpy (paddedname, name.c_str (), namelen);
2521  os.write (paddedname, paddedlength);
2522  }
2523 
2524  // data element
2525  if (tc.is_string ())
2526  {
2527  charNDArray chm = tc.char_array_value ();
2528  octave_idx_type nel = chm.numel ();
2529  octave_idx_type len = nel*2;
2530  octave_idx_type paddedlength = PAD (len);
2531 
2532  OCTAVE_LOCAL_BUFFER (int16_t, buf, nel+3);
2533  write_mat5_tag (os, miUINT16, len);
2534 
2535  const char *s = chm.data ();
2536 
2537  for (octave_idx_type i = 0; i < nel; i++)
2538  buf[i] = *s++ & 0x00FF;
2539 
2540  os.write (reinterpret_cast<char *> (buf), len);
2541 
2542  if (paddedlength > len)
2543  {
2544  static char padbuf[9]="\x00\x00\x00\x00\x00\x00\x00\x00";
2545  os.write (padbuf, paddedlength - len);
2546  }
2547  }
2548  else if (tc.is_sparse_type ())
2549  {
2550  if (tc.is_complex_type ())
2551  {
2553  octave_idx_type nnz = m.nnz ();
2554  octave_idx_type nc = m.cols ();
2555 
2556  write_mat5_sparse_index_vector (os, m.ridx (), nnz);
2557  write_mat5_sparse_index_vector (os, m.cidx (), nc + 1);
2558 
2559  NDArray buf (dim_vector (nnz, 1));
2560 
2561  for (octave_idx_type i = 0; i < nnz; i++)
2562  buf (i) = std::real (m.data (i));
2563 
2564  write_mat5_array (os, buf, save_as_floats);
2565 
2566  for (octave_idx_type i = 0; i < nnz; i++)
2567  buf (i) = std::imag (m.data (i));
2568 
2569  write_mat5_array (os, buf, save_as_floats);
2570  }
2571  else
2572  {
2573  const SparseMatrix m = tc.sparse_matrix_value ();
2574  octave_idx_type nnz = m.nnz ();
2575  octave_idx_type nc = m.cols ();
2576 
2577  write_mat5_sparse_index_vector (os, m.ridx (), nnz);
2578  write_mat5_sparse_index_vector (os, m.cidx (), nc + 1);
2579 
2580  // FIXME
2581  // Is there a way to easily do without this buffer
2582  NDArray buf (dim_vector (nnz, 1));
2583 
2584  for (int i = 0; i < nnz; i++)
2585  buf (i) = m.data (i);
2586 
2587  write_mat5_array (os, buf, save_as_floats);
2588  }
2589  }
2590  else if (cname == "int8")
2591  {
2592  int8NDArray m = tc.int8_array_value ();
2593 
2594  write_mat5_integer_data (os, m.fortran_vec (), -1, m.numel ());
2595  }
2596  else if (cname == "int16")
2597  {
2598  int16NDArray m = tc.int16_array_value ();
2599 
2600  write_mat5_integer_data (os, m.fortran_vec (), -2, m.numel ());
2601  }
2602  else if (cname == "int32")
2603  {
2604  int32NDArray m = tc.int32_array_value ();
2605 
2606  write_mat5_integer_data (os, m.fortran_vec (), -4, m.numel ());
2607  }
2608  else if (cname == "int64")
2609  {
2610  int64NDArray m = tc.int64_array_value ();
2611 
2612  write_mat5_integer_data (os, m.fortran_vec (), -8, m.numel ());
2613  }
2614  else if (cname == "uint8")
2615  {
2616  uint8NDArray m = tc.uint8_array_value ();
2617 
2618  write_mat5_integer_data (os, m.fortran_vec (), 1, m.numel ());
2619  }
2620  else if (cname == "uint16")
2621  {
2623 
2624  write_mat5_integer_data (os, m.fortran_vec (), 2, m.numel ());
2625  }
2626  else if (cname == "uint32")
2627  {
2629 
2630  write_mat5_integer_data (os, m.fortran_vec (), 4, m.numel ());
2631  }
2632  else if (cname == "uint64")
2633  {
2635 
2636  write_mat5_integer_data (os, m.fortran_vec (), 8, m.numel ());
2637  }
2638  else if (tc.is_bool_type ())
2639  {
2640  uint8NDArray m (tc.bool_array_value ());
2641 
2642  write_mat5_integer_data (os, m.fortran_vec (), 1, m.numel ());
2643  }
2644  else if (tc.is_real_scalar () || tc.is_real_matrix () || tc.is_range ())
2645  {
2646  if (tc.is_single_type ())
2647  {
2648  FloatNDArray m = tc.float_array_value ();
2649 
2650  write_mat5_array (os, m, save_as_floats);
2651  }
2652  else
2653  {
2654  NDArray m = tc.array_value ();
2655 
2656  write_mat5_array (os, m, save_as_floats);
2657  }
2658  }
2659  else if (tc.is_cell ())
2660  {
2661  Cell cell = tc.cell_value ();
2662 
2663  if (! write_mat5_cell_array (os, cell, mark_as_global, save_as_floats))
2664  goto error_cleanup;
2665  }
2666  else if (tc.is_complex_scalar () || tc.is_complex_matrix ())
2667  {
2668  if (tc.is_single_type ())
2669  {
2671 
2672  write_mat5_array (os, ::real (m_cmplx), save_as_floats);
2673  write_mat5_array (os, ::imag (m_cmplx), save_as_floats);
2674  }
2675  else
2676  {
2677  ComplexNDArray m_cmplx = tc.complex_array_value ();
2678 
2679  write_mat5_array (os, ::real (m_cmplx), save_as_floats);
2680  write_mat5_array (os, ::imag (m_cmplx), save_as_floats);
2681  }
2682  }
2683  else if (tc.is_map () || tc.is_inline_function () || tc.is_object ())
2684  {
2685  if (tc.is_inline_function () || tc.is_object ())
2686  {
2687  std::string classname = tc.is_object () ? tc.class_name () : "inline";
2688  size_t namelen = classname.length ();
2689 
2690  if (namelen > max_namelen)
2691  namelen = max_namelen; // Truncate names if necessary
2692 
2693  int paddedlength = PAD (namelen);
2694 
2695  write_mat5_tag (os, miINT8, namelen);
2696  OCTAVE_LOCAL_BUFFER (char, paddedname, paddedlength);
2697  memset (paddedname, 0, paddedlength);
2698  strncpy (paddedname, classname.c_str (), namelen);
2699  os.write (paddedname, paddedlength);
2700  }
2701 
2702  octave_map m;
2703 
2704  if (tc.is_object ()
2706  "saveobj") != std::string ())
2707  {
2708  octave_value_list tmp = feval ("saveobj", tc, 1);
2709  if (! error_state)
2710  m = tmp(0).map_value ();
2711  else
2712  goto error_cleanup;
2713  }
2714  else
2715  m = tc.map_value ();
2716 
2717  // an Octave structure */
2718  // recursively write each element of the structure
2719  {
2720  char buf[64];
2721  int32_t maxfieldnamelength = max_namelen + 1;
2722 
2723  octave_idx_type nf = m.nfields ();
2724 
2725  write_mat5_tag (os, miINT32, 4);
2726  os.write (reinterpret_cast<char *> (&maxfieldnamelength), 4);
2727  write_mat5_tag (os, miINT8, nf*maxfieldnamelength);
2728 
2729  // Iterating over the list of keys will preserve the order of
2730  // the fields.
2731  string_vector keys = m.keys ();
2732 
2733  for (octave_idx_type i = 0; i < nf; i++)
2734  {
2735  std::string key = keys(i);
2736 
2737  // write the name of each element
2738  memset (buf, 0, max_namelen + 1);
2739  // only 31 or 63 char names permitted
2740  strncpy (buf, key.c_str (), max_namelen);
2741  os.write (buf, max_namelen + 1);
2742  }
2743 
2744  octave_idx_type len = m.numel ();
2745 
2746  // Create temporary copy of structure contents to avoid
2747  // multiple calls of the contents method.
2748  std::vector<const octave_value *> elts (nf);
2749  for (octave_idx_type i = 0; i < nf; i++)
2750  elts[i] = m.contents (keys(i)).data ();
2751 
2752  for (octave_idx_type j = 0; j < len; j++)
2753  {
2754  // write the data of each element
2755 
2756  // Iterating over the list of keys will preserve the order
2757  // of the fields.
2758  for (octave_idx_type i = 0; i < nf; i++)
2759  {
2760  bool retval2 = save_mat5_binary_element (os, elts[i][j], "",
2761  mark_as_global,
2762  false,
2763  save_as_floats);
2764  if (! retval2)
2765  goto error_cleanup;
2766  }
2767  }
2768  }
2769  }
2770  else
2771  gripe_wrong_type_arg ("save", tc, false);
2772 
2773 skip_to_next:
2774  return true;
2775 
2776 error_cleanup:
2777  error ("save: error while writing '%s' to MAT file", name.c_str ());
2778 
2779  return false;
2780 }
save_type
Definition: data-conv.h:83
uint8NDArray uint8_array_value(void) const
Definition: ov.h:882
string_vector keys(void) const
Definition: oct-map.h:339
bool is_object(void) const
Definition: ov.h:577
ComplexNDArray complex_array_value(bool frc_str_conv=false) const
Definition: ov.h:798
static std::string system_path(void)
Definition: load-path.h:278
const Cell & contents(const_iterator p) const
Definition: oct-map.h:314
bool is_range(void) const
Definition: ov.h:571
octave_idx_type cols(void) const
Definition: Sparse.h:264
std::string find_first_of(const string_vector &names)
Definition: pathsearch.cc:132
#define INT_LEN(nel, size)
Definition: Cell.h:35
octave_idx_type nfields(void) const
Definition: oct-map.h:205
charNDArray char_array_value(bool frc_str_conv=false) const
Definition: ov.h:817
Definition: ls-mat5.h:28
T * data(void)
Definition: Sparse.h:509
void gripe_wrong_type_arg(const char *name, const char *s, bool is_error)
Definition: gripes.cc:135
octave_function * load_fcn_from_file(const std::string &file_name, const std::string &dir_name, const std::string &dispatch_type, const std::string &package_name, const std::string &fcn_name, bool autoload)
Definition: oct-parse.cc:8198
static void read_int(std::istream &is, bool swap, int32_t &val)
Definition: ls-mat5.cc:475
int ndims(void) const
Definition: ov.h:479
void assign(const std::string &k, const Cell &val)
Definition: oct-map.h:348
int8NDArray int8_array_value(void) const
Definition: ov.h:870
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:275
int16NDArray int16_array_value(void) const
Definition: ov.h:873
const octave_value & contents(const_iterator p) const
Definition: oct-map.h:192
const_iterator iterator
Definition: oct-map.h:302
octave_map map_value(void) const
Definition: ov.cc:1585
save_type get_save_type(double, double)
Definition: ls-utils.cc:35
arrayclasstype
Definition: ls-mat5.cc:99
static void set_scope(scope_id scope)
Definition: symtab.h:1169
bool is_complex_scalar(void) const
Definition: ov.h:541
void run(size_t num)
void resize(int n, int fill_value=0)
Definition: dim-vector.h:287
uint64NDArray uint64_array_value(void) const
Definition: ov.h:891
void swap_bytes< 8 >(void *ptr)
Definition: byte-swap.h:67
void error(const char *fmt,...)
Definition: error.cc:476
int32NDArray int32_array_value(void) const
Definition: ov.h:876
octave_value_list feval(const std::string &name, const octave_value_list &args, int nargout)
Definition: oct-parse.cc:8625
static string_vector names(const map_type &lst)
Definition: help.cc:782
#define PAD(l)
Definition: ls-mat5.cc:85
static scope_id alloc_scope(void)
Definition: symtab.h:1167
static octave_value subsys_ov
Definition: ls-mat5.cc:90
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)
Definition: ls-mat5.cc:2333
bool is_cell(void) const
Definition: ov.h:529
octave_idx_type * cidx(void)
Definition: Sparse.h:531
octave_value_list eval_string(const std::string &eval_str, bool silent, int &parse_status, int nargout)
Definition: oct-parse.cc:8810
octave_idx_type numel(void) const
Definition: oct-map.h:372
octave_value fcn_val(void) const
octave_fcn_handle * fcn_handle_value(bool silent=false) const
Definition: ov.cc:1621
std::string read_mat5_binary_element(std::istream &is, const std::string &filename, bool swap, bool &global, octave_value &tc)
Definition: ls-mat5.cc:492
#define MAT5_DO_WRITE(TYPE, data, count, stream)
Definition: ls-mat5.cc:1674
static void write_mat5_array(std::ostream &os, const NDArray &m, bool save_as_floats)
Definition: ls-mat5.cc:1688
const_iterator end(void) const
Definition: oct-map.h:305
boolNDArray bool_array_value(bool warn=false) const
Definition: ov.h:811
octave_idx_type numel(int n=0) const
Number of elements that a matrix with this dimensions would have.
Definition: dim-vector.h:361
octave_idx_type nnz(void) const
Definition: Sparse.h:248
octave_idx_type nzmax(void) const
Definition: Sparse.h:246
int64NDArray int64_array_value(void) const
Definition: ov.h:879
Cell cell_value(void) const
Definition: ov.cc:1566
bool all_integers(double &max_val, double &min_val) const
Definition: dNDArray.cc:597
FloatNDArray float_array_value(bool frc_str_conv=false) const
Definition: ov.h:782
void swap_bytes< 4 >(void *ptr)
Definition: byte-swap.h:59
void add_fcn(void(*fcn)(void))
#define READ_INTEGER_DATA(TYPE, swap, data, size, len, stream)
const_iterator begin(void) const
Definition: oct-map.h:182
bool reconstruct_parents(void)
Definition: ov-class.cc:1204
octave_value convert_to_str(bool pad=false, bool force=false, char type= '\'') const
Definition: ov.h:1017
bool is_complex_matrix(void) const
Definition: ov.h:544
std::string key(const_iterator p) const
Definition: oct-map.h:187
bool is_sparse_type(void) const
Definition: ov.h:666
octave_idx_type nfields(void) const
Definition: oct-map.h:327
#define READ_PAD(is_small_data_element, l)
Definition: ls-mat5.cc:84
bool is_bool_type(void) const
Definition: ov.h:645
bool is_real_scalar(void) const
Definition: ov.h:535
std::string string_value(bool force=false) const
Definition: ov.h:897
void write_mat5_integer_data(std::ostream &os, const T *m, int size, octave_idx_type nel)
Definition: ls-mat5.cc:1872
static void read_mat5_binary_data(std::istream &is, double *data, octave_idx_type count, bool swap, mat5_data_type type, oct_mach_info::float_format flt_fmt)
Definition: ls-mat5.cc:126
bool exists(void) const
Definition: file-stat.h:134
static std::string make_absolute(const std::string &s, const std::string &dot_path=get_current_directory())
Definition: oct-env.cc:132
FloatComplexNDArray float_complex_array_value(bool frc_str_conv=false) const
Definition: ov.h:802
static bool write_mat5_cell_array(std::ostream &os, const Cell &cell, bool mark_as_global, bool save_as_floats)
Definition: ls-mat5.cc:1963
#define INT8(l)
Definition: ls-mat5.cc:86
bool is_string(void) const
Definition: ov.h:562
void read_mat5_integer_data(std::istream &is, T *m, octave_idx_type count, bool swap, mat5_data_type type)
Definition: ls-mat5.cc:257
const T * data(void) const
Definition: Array.h:479
int error_state
Definition: error.cc:101
bool is_inline_function(void) const
Definition: ov.h:692
bool is_complex_type(void) const
Definition: ov.h:654
const_iterator end(void) const
Definition: oct-map.h:183
static void write_mat5_sparse_index_vector(std::ostream &os, const octave_idx_type *idx, octave_idx_type nel)
Definition: ls-mat5.cc:2308
static void assign(const std::string &name, const octave_value &value=octave_value(), scope_id scope=xcurrent_scope, context_id context=xdefault_context, bool force_add=false)
Definition: symtab.h:1335
static std::string find_method(const std::string &class_name, const std::string &meth, std::string &dir_name, const std::string &pack_name=std::string())
Definition: load-path.h:96
static void push(octave_function *f, symbol_table::scope_id scope=symbol_table::current_scope(), symbol_table::context_id context=symbol_table::current_context())
Definition: toplev.h:233
Definition: dMatrix.h:35
int read_mat5_binary_file_header(std::istream &is, bool &swap, bool quiet, const std::string &filename)
Definition: ls-mat5.cc:1570
size_t size(T const (&)[z])
Definition: help.cc:103
static int write_mat5_tag(std::ostream &is, int type, octave_idx_type bytes)
Definition: ls-mat5.cc:1648
static void gripe_dim_too_large(const std::string &name)
Definition: ls-mat5.cc:2323
SparseComplexMatrix sparse_complex_matrix_value(bool frc_str_conv=false) const
Definition: ov.h:824
void read_doubles(std::istream &is, double *data, save_type type, octave_idx_type len, bool swap, oct_mach_info::float_format fmt)
Definition: data-conv.cc:778
const_iterator begin(void) const
Definition: oct-map.h:304
dim_vector dims(void) const
Definition: ov.h:470
int save_mat5_element_length(const octave_value &tc, const std::string &name, bool save_as_floats, bool mat7_format)
Definition: ls-mat5.cc:2154
bool is_map(void) const
Definition: ov.h:574
void warning(const char *fmt,...)
Definition: error.cc:681
static std::string dir_sep_chars(void)
Definition: file-ops.h:68
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:233
void clear(void)
Definition: Array.cc:84
NDArray array_value(bool frc_str_conv=false) const
Definition: ov.h:779
octave_idx_type * ridx(void)
Definition: Sparse.h:518
bool xfinite(double x)
Definition: lo-mappers.cc:152
octave_scalar_map scalar_map_value(void) const
Definition: ov.cc:1591
#define OCTAVE_EXEC_PREFIX
Definition: defaults.h:64
mat5_data_type
Definition: ls-mat5.h:26
bool is_uint8_type(void) const
Definition: ov.h:631
T * xdata(void)
Definition: Sparse.h:511
static octave_value make_fcn_handle(octave_builtin::fcn ff, const std::string &nm)
Definition: ov-classdef.cc:129
bool all_integers(float &max_val, float &min_val) const
Definition: fNDArray.cc:557
#define OCTAVE_MAT5_INTEGER_READ(TYP)
Definition: ls-mat5.cc:372
std::string class_name(void) const
Definition: ov.h:1049
#define OCTAVE_LOCAL_BUFFER(T, buf, size)
Definition: oct-locbuf.h:197
static int read_mat5_tag(std::istream &is, bool swap, int32_t &type, int32_t &bytes, bool &is_small_data_element)
Definition: ls-mat5.cc:437
ColumnVector imag(const ComplexColumnVector &a)
Definition: dColVector.cc:162
std::complex< float > FloatComplex
Definition: oct-cmplx.h:30
static void pop(void)
Definition: toplev.h:332
SparseMatrix sparse_matrix_value(bool frc_str_conv=false) const
Definition: ov.h:820
std::complex< double > Complex
Definition: oct-cmplx.h:29
const T * fortran_vec(void) const
Definition: Array.h:481
octave_fields::const_iterator const_iterator
Definition: oct-map.h:301
bool is_single_type(void) const
Definition: ov.h:611
bool reconstruct_exemplar(void)
Definition: ov-class.cc:1139
ColumnVector real(const ComplexColumnVector &a)
Definition: dColVector.cc:156
Definition: ls-mat5.h:43
uint32NDArray uint32_array_value(void) const
Definition: ov.h:888
uint16NDArray uint16_array_value(void) const
Definition: ov.h:885
void read_floats(std::istream &is, float *data, save_type type, octave_idx_type len, bool swap, oct_mach_info::float_format fmt)
Definition: data-conv.cc:837
return octave_value(v1.char_array_value().concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string())? '\'': '"'))
bool too_large_for_float(void) const
Definition: dNDArray.cc:633
static void erase_scope(scope_id scope)
Definition: symtab.h:1218
bool is_real_matrix(void) const
Definition: ov.h:538
int save_mat5_array_length(const double *val, octave_idx_type nel, bool save_as_floats)
Definition: ls-mat5.cc:1981