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