GNU Octave 10.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
ls-mat5.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 1996-2025 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
92static 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
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)
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
127static void
128read_mat5_binary_data (std::istream& is, double *data,
129 octave_idx_type count, bool swap, mat5_data_type type,
130 octave::mach_info::float_format flt_fmt)
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
192static void
193read_mat5_binary_data (std::istream& is, float *data,
194 octave_idx_type count, bool swap, mat5_data_type type,
195 octave::mach_info::float_format flt_fmt)
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
257template <typename T>
258void
259read_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
329template void
331 octave_idx_type count, bool swap,
332 mat5_data_type type);
333
334template void
336 octave_idx_type count, bool swap,
337 mat5_data_type type);
338
339template void
341 octave_idx_type count, bool swap,
342 mat5_data_type type);
343
344template void
346 octave_idx_type count, bool swap,
347 mat5_data_type type);
348
349template void
351 octave_idx_type count, bool swap,
352 mat5_data_type type);
353
354template void
356 octave_idx_type count, bool swap,
357 mat5_data_type type);
358
359template void
361 octave_idx_type count, bool swap,
362 mat5_data_type type);
363
364template void
366 octave_idx_type count, bool swap,
367 mat5_data_type type);
368
369template void
370read_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.rwdata (), 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.rwdata (), 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
426static int
427read_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
461static void
462read_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
478std::string
479read_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
499 = (octave::mach_info::native_float_format ()
500 == octave::mach_info::flt_fmt_ieee_big_endian);
501
502 // MAT files always use IEEE floating point
503 octave::mach_info::float_format flt_fmt = octave::mach_info::flt_fmt_unknown;
504 if ((flt_fmt_is_big_endian && ! swap) || (! flt_fmt_is_big_endian && swap))
505 flt_fmt = octave::mach_info::flt_fmt_ieee_big_endian;
506 else
507 flt_fmt = octave::mach_info::flt_fmt_ieee_little_endian;
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 {
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.rwdata ();
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.rwdata (), 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
899 = (octave::config::octave_exec_home ()
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 ();
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
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
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
1267 break;
1268
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 {
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
1293 break;
1294
1297 break;
1298
1301 break;
1302
1305 break;
1306
1309 break;
1310
1313 break;
1314
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.rwdata (), 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.rwdata (), 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
1369 // handle as a numerical array to start with
1370
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.rwdata (), 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.rwdata (), 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
1522data_read_error:
1523 error ("load: trouble reading binary file '%s'", filename.c_str ());
1524
1525skip_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
1532int
1533read_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
1613static int
1614write_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.
1650static void
1651write_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
1746static void
1747write_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
1831template <typename T>
1832void
1833write_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
1884template void
1885write_mat5_integer_data (std::ostream& os, const octave_int8 *m,
1886 int size, octave_idx_type nel);
1887
1888template void
1889write_mat5_integer_data (std::ostream& os, const octave_int16 *m,
1890 int size, octave_idx_type nel);
1891
1892template void
1893write_mat5_integer_data (std::ostream& os, const octave_int32 *m,
1894 int size, octave_idx_type nel);
1895
1896template void
1897write_mat5_integer_data (std::ostream& os, const octave_int64 *m,
1898 int size, octave_idx_type nel);
1899
1900template void
1901write_mat5_integer_data (std::ostream& os, const octave_uint8 *m,
1902 int size, octave_idx_type nel);
1903
1904template void
1905write_mat5_integer_data (std::ostream& os, const octave_uint16 *m,
1906 int size, octave_idx_type nel);
1907
1908template void
1909write_mat5_integer_data (std::ostream& os, const octave_uint32 *m,
1910 int size, octave_idx_type nel);
1911
1912template void
1913write_mat5_integer_data (std::ostream& os, const octave_uint64 *m,
1914 int size, octave_idx_type nel);
1915
1916template void
1917write_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
1923static bool
1924write_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
1941int
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
2015int
2016save_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
2071int
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
2092int
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
2113static uint16_t *
2114maybe_convert_to_u16 (const charNDArray& chm, std::size_t& n16_str)
2115{
2116 uint16_t *u16_str;
2117 const 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
2131int
2132save_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
2294static void
2295write_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
2309static void
2310warn_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
2320bool
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 const 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 {
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
const dim_vector & dims() const
Return a const-reference so that dims ()(i) works efficiently.
Definition Array.h:507
void clear()
bool isvector() const
Size of the specified dimension.
Definition Array.h:655
int ndims() const
Size of the specified dimension.
Definition Array.h:679
const T * data() const
Size of the specified dimension.
Definition Array.h:665
T * rwdata()
Size of the specified dimension.
octave_idx_type numel() const
Number of elements in the array.
Definition Array.h:418
Definition Cell.h:41
bool all_integers(float &max_val, float &min_val) const
Definition fNDArray.cc:308
bool all_integers(double &max_val, double &min_val) const
Definition dNDArray.cc:351
bool too_large_for_float() const
Definition dNDArray.cc:387
octave_idx_type cols() const
Definition Sparse.h:349
octave_idx_type nzmax() const
Amount of storage for nonzero elements.
Definition Sparse.h:333
octave_idx_type * cidx()
Definition Sparse.h:593
T * data()
Definition Sparse.h:571
T * xdata()
Definition Sparse.h:573
octave_idx_type * ridx()
Definition Sparse.h:580
octave_idx_type nnz() const
Actual number of nonzero terms.
Definition Sparse.h:336
Vector representing the dimensions (size) of an Array.
Definition dim-vector.h:90
octave_idx_type numel(int n=0) const
Number of elements that a matrix with this dimensions would have.
Definition dim-vector.h:331
void resize(int n, int fill_value=0)
Definition dim-vector.h:268
bool reconstruct_parents()
Definition ov-class.cc:1159
bool reconstruct_exemplar()
Definition ov-class.cc:1080
octave_value fcn_val()
octave_idx_type nfields() const
Definition oct-map.h:323
const_iterator end() const
Definition oct-map.h:298
string_vector keys() const
Definition oct-map.h:335
const Cell & contents(const_iterator p) const
Definition oct-map.h:310
octave_idx_type numel() const
Definition oct-map.h:368
void assign(const std::string &k, const Cell &val)
Definition oct-map.h:344
const_iterator begin() const
Definition oct-map.h:297
const octave_value & contents(const_iterator p) const
Definition oct-map.h:197
const_iterator end() const
Definition oct-map.h:185
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:900
SparseMatrix sparse_matrix_value(bool frc_str_conv=false) const
Definition ov.h:909
std::string class_name() const
Definition ov.h:1362
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:965
uint16NDArray uint16_array_value() const
Definition ov.h:974
int16NDArray int16_array_value() const
Definition ov.h:962
Cell cell_value() const
int8NDArray int8_array_value() const
Definition ov.h:959
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:884
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:906
bool isempty() const
Definition ov.h:601
bool is_uint8_type() const
Definition ov.h:718
uint64NDArray uint64_array_value() const
Definition ov.h:980
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
std::string string_value(bool force=false) const
Definition ov.h:983
bool iscomplex() const
Definition ov.h:741
octave_fcn_handle * fcn_handle_value(bool silent=false) const
int64NDArray int64_array_value() const
Definition ov.h:968
NDArray array_value(bool frc_str_conv=false) const
Definition ov.h:865
octave_value convert_to_str(bool pad=false, bool force=false, char type='\'') const
Definition ov.h:1322
uint8NDArray uint8_array_value() const
Definition ov.h:971
FloatComplexNDArray float_complex_array_value(bool frc_str_conv=false) const
Definition ov.h:888
FloatNDArray float_array_value(bool frc_str_conv=false) const
Definition ov.h:868
bool isstruct() const
Definition ov.h:649
uint32NDArray uint32_array_value() const
Definition ov.h:977
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:913
bool islogical() const
Definition ov.h:735
dim_vector dims() const
Definition ov.h:541
ColumnVector real(const ComplexColumnVector &a)
ColumnVector imag(const ComplexColumnVector &a)
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:802
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:862
save_type
Definition data-conv.h:85
@ LS_U_CHAR
Definition data-conv.h:86
@ LS_DOUBLE
Definition data-conv.h:93
@ LS_LONG
Definition data-conv.h:95
@ LS_U_LONG
Definition data-conv.h:94
@ LS_U_SHORT
Definition data-conv.h:87
@ LS_FLOAT
Definition data-conv.h:92
@ LS_SHORT
Definition data-conv.h:90
@ LS_CHAR
Definition data-conv.h:89
@ LS_INT
Definition data-conv.h:91
@ LS_U_INT
Definition data-conv.h:88
void warning_with_id(const char *id, const char *fmt,...)
Definition error.cc:1093
void error(const char *fmt,...)
Definition error.cc:1003
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
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
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 *)
uint8_t * octave_u32_to_u8_wrapper(const uint32_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_u16_to_u8_wrapper(const uint16_t *src, size_t src_len, uint8_t *result_buf, size_t *lengthp)
F77_RET_T len
Definition xerbla.cc:61