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