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