comparison libinterp/interp-core/ls-mat5.cc @ 15195:2fc554ffbc28

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