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