1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2004-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25
26 // This file should not include config.h. It is only included in other
27 // C++ source files that should have included config.h before including
28 // this file.
29
30 #include <istream>
31 #include <limits>
32 #include <ostream>
33 #include <sstream>
34 #include <vector>
35
36 #include "dNDArray.h"
37 #include "fNDArray.h"
38 #include "int8NDArray.h"
39 #include "int16NDArray.h"
40 #include "int32NDArray.h"
41 #include "int64NDArray.h"
42 #include "uint8NDArray.h"
43 #include "uint16NDArray.h"
44 #include "uint32NDArray.h"
45 #include "uint64NDArray.h"
46
47 #include "lo-ieee.h"
48 #include "lo-utils.h"
49 #include "mx-base.h"
50 #include "quit.h"
51 #include "oct-locbuf.h"
52
53 #include "defun.h"
54 #include "errwarn.h"
55 #include "ovl.h"
56 #include "oct-lvalue.h"
57 #include "oct-hdf5.h"
58 #include "oct-stream.h"
59 #include "ops.h"
60 #include "ov-base.h"
61 #include "ov-base-mat.h"
62 #include "ov-base-mat.cc"
63 #include "ov-base-scalar.h"
64 #include "ov-base-scalar.cc"
65 #include "ov-base-int.h"
66 #include "ov-int-traits.h"
67 #include "pr-output.h"
68 #include "variables.h"
69
70 #include "byte-swap.h"
71 #include "ls-oct-text.h"
72 #include "ls-utils.h"
73 #include "ls-hdf5.h"
74
75 // We have all the machinery below (octave_base_int_helper and
76 // octave_base_int_helper_traits) to avoid a few warnings from GCC
77 // about comparisons always false due to limited range of data types.
78 // Ugh. The cure may be worse than the disease.
79
80 template <typename T, bool is_signed = true, bool can_be_too_big = true>
81 struct octave_base_int_helper
82 {
83 static bool
char_value_out_of_rangeoctave_base_int_helper84 char_value_out_of_range (T val)
85 {
86 return val < 0 || val > std::numeric_limits<unsigned char>::max ();
87 }
88 };
89
90 template <typename T>
91 struct octave_base_int_helper<T, false, false>
92 {
char_value_out_of_rangeoctave_base_int_helper93 static bool char_value_out_of_range (T) { return false; }
94 };
95
96 template <typename T>
97 struct octave_base_int_helper<T, false, true>
98 {
char_value_out_of_rangeoctave_base_int_helper99 static bool char_value_out_of_range (T val)
100 {
101 return val > std::numeric_limits<unsigned char>::max ();
102 }
103 };
104
105 template <typename T>
106 struct octave_base_int_helper<T, true, false>
107 {
char_value_out_of_rangeoctave_base_int_helper108 static bool char_value_out_of_range (T val) { return val < 0; }
109 };
110
111 // For all types other than char, signed char, and unsigned char, we
112 // assume that the upper limit for the range of allowable values is
113 // larger than the range for unsigned char. If that's not true, we
114 // are still OK, but will see the warnings again for any other types
115 // that do not meet this assumption.
116
117 template <typename T>
118 struct octave_base_int_helper_traits
119 {
120 static const bool can_be_larger_than_uchar_max = true;
121 };
122
123 template <>
124 struct octave_base_int_helper_traits<char>
125 {
126 static const bool can_be_larger_than_uchar_max = false;
127 };
128
129 template <>
130 struct octave_base_int_helper_traits<signed char>
131 {
132 static const bool can_be_larger_than_uchar_max = false;
133 };
134
135 template <>
136 struct octave_base_int_helper_traits<unsigned char>
137 {
138 static const bool can_be_larger_than_uchar_max = false;
139 };
140
141 template <typename T>
142 octave_base_value *
try_narrowing_conversion(void)143 octave_base_int_matrix<T>::try_narrowing_conversion (void)
144 {
145 octave_base_value *retval = nullptr;
146
147 if (this->matrix.numel () == 1)
148 retval = new typename octave_value_int_traits<T>::scalar_type
149 (this->matrix (0));
150
151 return retval;
152 }
153
154 template <typename T>
155 octave_value
convert_to_str_internal(bool,bool,char type) const156 octave_base_int_matrix<T>::convert_to_str_internal (bool, bool, char type) const
157 {
158 octave_value retval;
159 dim_vector dv = this->dims ();
160 octave_idx_type nel = dv.numel ();
161
162 charNDArray chm (dv);
163
164 bool warned = false;
165
166 for (octave_idx_type i = 0; i < nel; i++)
167 {
168 octave_quit ();
169
170 typename T::element_type tmp = this->matrix(i);
171
172 typedef typename T::element_type::val_type val_type;
173
174 val_type ival = tmp.value ();
175
176 static const bool is_signed = std::numeric_limits<val_type>::is_signed;
177 static const bool can_be_larger_than_uchar_max
178 = octave_base_int_helper_traits<val_type>::can_be_larger_than_uchar_max;
179
180 if (octave_base_int_helper<val_type, is_signed,
181 can_be_larger_than_uchar_max>::char_value_out_of_range (ival))
182 {
183 // FIXME: is there something better we could do?
184
185 ival = 0;
186
187 if (! warned)
188 {
189 ::warning ("range error for conversion to character value");
190 warned = true;
191 }
192 }
193 else
194 chm (i) = static_cast<char> (ival);
195 }
196
197 retval = octave_value (chm, type);
198
199 return retval;
200 }
201
202 template <typename MT>
203 octave_value
as_double(void) const204 octave_base_int_matrix<MT>::as_double (void) const
205 {
206 return NDArray (this->matrix);
207 }
208
209 template <typename MT>
210 octave_value
as_single(void) const211 octave_base_int_matrix<MT>::as_single (void) const
212 {
213 return FloatNDArray (this->matrix);
214 }
215
216 template <typename MT>
217 octave_value
as_int8(void) const218 octave_base_int_matrix<MT>::as_int8 (void) const
219 {
220 return int8NDArray (this->matrix);
221 }
222
223 template <typename MT>
224 octave_value
as_int16(void) const225 octave_base_int_matrix<MT>::as_int16 (void) const
226 {
227 return int16NDArray (this->matrix);
228 }
229
230 template <typename MT>
231 octave_value
as_int32(void) const232 octave_base_int_matrix<MT>::as_int32 (void) const
233 {
234 return int32NDArray (this->matrix);
235 }
236
237 template <typename MT>
238 octave_value
as_int64(void) const239 octave_base_int_matrix<MT>::as_int64 (void) const
240 {
241 return int64NDArray (this->matrix);
242 }
243
244 template <typename MT>
245 octave_value
as_uint8(void) const246 octave_base_int_matrix<MT>::as_uint8 (void) const
247 {
248 return uint8NDArray (this->matrix);
249 }
250
251 template <typename MT>
252 octave_value
as_uint16(void) const253 octave_base_int_matrix<MT>::as_uint16 (void) const
254 {
255 return uint16NDArray (this->matrix);
256 }
257
258 template <typename MT>
259 octave_value
as_uint32(void) const260 octave_base_int_matrix<MT>::as_uint32 (void) const
261 {
262 return uint32NDArray (this->matrix);
263 }
264
265 template <typename MT>
266 octave_value
as_uint64(void) const267 octave_base_int_matrix<MT>::as_uint64 (void) const
268 {
269 return uint64NDArray (this->matrix);
270 }
271
272 template <typename T>
273 std::string
edit_display(const float_display_format & fmt,octave_idx_type i,octave_idx_type j) const274 octave_base_int_matrix<T>::edit_display (const float_display_format& fmt,
275 octave_idx_type i,
276 octave_idx_type j) const
277 {
278 std::ostringstream buf;
279 octave_print_internal (buf, fmt, this->matrix(i,j));
280 return buf.str ();
281 }
282
283 template <typename T>
284 bool
save_ascii(std::ostream & os)285 octave_base_int_matrix<T>::save_ascii (std::ostream& os)
286 {
287 dim_vector dv = this->dims ();
288
289 os << "# ndims: " << dv.ndims () << "\n";
290
291 for (int i = 0; i < dv.ndims (); i++)
292 os << ' ' << dv(i);
293
294 os << "\n" << this->matrix;
295
296 return true;
297 }
298
299 template <typename T>
300 bool
load_ascii(std::istream & is)301 octave_base_int_matrix<T>::load_ascii (std::istream& is)
302 {
303 int mdims = 0;
304
305 if (! extract_keyword (is, "ndims", mdims, true))
306 error ("load: failed to extract number of dimensions");
307
308 if (mdims < 0)
309 error ("load: failed to extract number of rows and columns");
310
311 dim_vector dv;
312 dv.resize (mdims);
313
314 for (int i = 0; i < mdims; i++)
315 is >> dv(i);
316
317 T tmp(dv);
318
319 is >> tmp;
320
321 if (! is)
322 error ("load: failed to load matrix constant");
323
324 this->matrix = tmp;
325
326 return true;
327 }
328
329 template <typename T>
330 bool
save_binary(std::ostream & os,bool)331 octave_base_int_matrix<T>::save_binary (std::ostream& os, bool)
332 {
333 dim_vector dv = this->dims ();
334 if (dv.ndims () < 1)
335 return false;
336
337 // Use negative value for ndims to differentiate with old format!!
338 int32_t tmp = - dv.ndims ();
339 os.write (reinterpret_cast<char *> (&tmp), 4);
340 for (int i=0; i < dv.ndims (); i++)
341 {
342 tmp = dv(i);
343 os.write (reinterpret_cast<char *> (&tmp), 4);
344 }
345
346 os.write (reinterpret_cast<const char *> (this->matrix.data ()),
347 this->byte_size ());
348
349 return true;
350 }
351
352 template <typename T>
353 bool
load_binary(std::istream & is,bool swap,octave::mach_info::float_format)354 octave_base_int_matrix<T>::load_binary (std::istream& is, bool swap,
355 octave::mach_info::float_format)
356 {
357 int32_t mdims;
358 if (! is.read (reinterpret_cast<char *> (&mdims), 4))
359 return false;
360 if (swap)
361 swap_bytes<4> (&mdims);
362 if (mdims >= 0)
363 return false;
364
365 mdims = - mdims;
366 int32_t di;
367 dim_vector dv;
368 dv.resize (mdims);
369
370 for (int i = 0; i < mdims; i++)
371 {
372 if (! is.read (reinterpret_cast<char *> (&di), 4))
373 return false;
374 if (swap)
375 swap_bytes<4> (&di);
376 dv(i) = di;
377 }
378
379 // Convert an array with a single dimension to be a row vector.
380 // Octave should never write files like this, other software
381 // might.
382
383 if (mdims == 1)
384 {
385 mdims = 2;
386 dv.resize (mdims);
387 dv(1) = dv(0);
388 dv(0) = 1;
389 }
390
391 T m (dv);
392
393 if (! is.read (reinterpret_cast<char *> (m.fortran_vec ()), m.byte_size ()))
394 return false;
395
396 if (swap)
397 {
398 int nel = dv.numel ();
399 int bytes = nel / m.byte_size ();
400 for (int i = 0; i < nel; i++)
401 switch (bytes)
402 {
403 case 8:
404 swap_bytes<8> (&m(i));
405 break;
406 case 4:
407 swap_bytes<4> (&m(i));
408 break;
409 case 2:
410 swap_bytes<2> (&m(i));
411 break;
412 case 1:
413 default:
414 break;
415 }
416 }
417
418 this->matrix = m;
419 return true;
420 }
421
422 template <typename T>
423 bool
save_hdf5_internal(octave_hdf5_id loc_id,octave_hdf5_id save_type,const char * name,bool)424 octave_base_int_matrix<T>::save_hdf5_internal (octave_hdf5_id loc_id,
425 octave_hdf5_id save_type,
426 const char *name, bool)
427 {
428 bool retval = false;
429
430 #if defined (HAVE_HDF5)
431
432 hid_t save_type_hid = save_type;
433 dim_vector dv = this->dims ();
434 int empty = save_hdf5_empty (loc_id, name, dv);
435 if (empty)
436 return (empty > 0);
437
438 int rank = dv.ndims ();
439 hid_t space_hid, data_hid;
440 space_hid = data_hid = -1;
441 OCTAVE_LOCAL_BUFFER (hsize_t, hdims, rank);
442
443 // Octave uses column-major, while HDF5 uses row-major ordering
444 for (int i = 0; i < rank; i++)
445 hdims[i] = dv(rank-i-1);
446
447 space_hid = H5Screate_simple (rank, hdims, nullptr);
448
449 if (space_hid < 0) return false;
450 #if defined (HAVE_HDF5_18)
451 data_hid = H5Dcreate (loc_id, name, save_type_hid, space_hid,
452 octave_H5P_DEFAULT, octave_H5P_DEFAULT, octave_H5P_DEFAULT);
453 #else
454 data_hid = H5Dcreate (loc_id, name, save_type_hid, space_hid,
455 octave_H5P_DEFAULT);
456 #endif
457 if (data_hid < 0)
458 {
459 H5Sclose (space_hid);
460 return false;
461 }
462
463 retval = H5Dwrite (data_hid, save_type_hid, octave_H5S_ALL, octave_H5S_ALL,
464 octave_H5P_DEFAULT, this->matrix.data ()) >= 0;
465
466 H5Dclose (data_hid);
467 H5Sclose (space_hid);
468
469 #else
470 octave_unused_parameter (loc_id);
471 octave_unused_parameter (save_type);
472 octave_unused_parameter (name);
473
474 this->warn_save ("hdf5");
475 #endif
476
477 return retval;
478 }
479
480 template <typename T>
481 bool
load_hdf5_internal(octave_hdf5_id loc_id,octave_hdf5_id save_type,const char * name)482 octave_base_int_matrix<T>::load_hdf5_internal (octave_hdf5_id loc_id,
483 octave_hdf5_id save_type,
484 const char *name)
485 {
486 bool retval = false;
487
488 #if defined (HAVE_HDF5)
489
490 hid_t save_type_hid = save_type;
491 dim_vector dv;
492 int empty = load_hdf5_empty (loc_id, name, dv);
493 if (empty > 0)
494 this->matrix.resize (dv);
495 if (empty)
496 return (empty > 0);
497
498 #if defined (HAVE_HDF5_18)
499 hid_t data_hid = H5Dopen (loc_id, name, octave_H5P_DEFAULT);
500 #else
501 hid_t data_hid = H5Dopen (loc_id, name);
502 #endif
503 hid_t space_id = H5Dget_space (data_hid);
504
505 hsize_t rank = H5Sget_simple_extent_ndims (space_id);
506
507 if (rank < 1)
508 {
509 H5Sclose (space_id);
510 H5Dclose (data_hid);
511 return false;
512 }
513
514 OCTAVE_LOCAL_BUFFER (hsize_t, hdims, rank);
515 OCTAVE_LOCAL_BUFFER (hsize_t, maxdims, rank);
516
517 H5Sget_simple_extent_dims (space_id, hdims, maxdims);
518
519 // Octave uses column-major, while HDF5 uses row-major ordering
520 if (rank == 1)
521 {
522 dv.resize (2);
523 dv(0) = 1;
524 dv(1) = hdims[0];
525 }
526 else
527 {
528 dv.resize (rank);
529 for (hsize_t i = 0, j = rank - 1; i < rank; i++, j--)
530 dv(j) = hdims[i];
531 }
532
533 T m (dv);
534 if (H5Dread (data_hid, save_type_hid, octave_H5S_ALL, octave_H5S_ALL,
535 octave_H5P_DEFAULT, m.fortran_vec ()) >= 0)
536 {
537 retval = true;
538 this->matrix = m;
539 }
540
541 H5Sclose (space_id);
542 H5Dclose (data_hid);
543
544 #else
545 octave_unused_parameter (loc_id);
546 octave_unused_parameter (save_type);
547 octave_unused_parameter (name);
548
549 this->warn_load ("hdf5");
550 #endif
551
552 return retval;
553 }
554
555 template <typename T>
556 void
print_raw(std::ostream & os,bool pr_as_read_syntax) const557 octave_base_int_matrix<T>::print_raw (std::ostream& os,
558 bool pr_as_read_syntax) const
559 {
560 octave_print_internal (os, this->matrix, pr_as_read_syntax,
561 this->current_print_indent_level ());
562 }
563
564 template <typename T>
565 octave_value
convert_to_str_internal(bool,bool,char type) const566 octave_base_int_scalar<T>::convert_to_str_internal (bool, bool, char type) const
567 {
568 octave_value retval;
569
570 T tmp = this->scalar;
571
572 typedef typename T::val_type val_type;
573
574 val_type ival = tmp.value ();
575
576 static const bool is_signed = std::numeric_limits<val_type>::is_signed;
577 static const bool can_be_larger_than_uchar_max
578 = octave_base_int_helper_traits<val_type>::can_be_larger_than_uchar_max;
579
580 if (octave_base_int_helper<val_type, is_signed,
581 can_be_larger_than_uchar_max>::char_value_out_of_range (ival))
582 {
583 // FIXME: is there something better we could do?
584
585 ival = 0;
586
587 ::warning ("range error for conversion to character value");
588 }
589 else
590 retval = octave_value (std::string (1, static_cast<char> (ival)), type);
591
592 return retval;
593 }
594
595 template <typename T>
596 octave_value
as_double(void) const597 octave_base_int_scalar<T>::as_double (void) const
598 {
599 return static_cast<double> (this->scalar);
600 }
601
602 template <typename T>
603 octave_value
as_single(void) const604 octave_base_int_scalar<T>::as_single (void) const
605 {
606 return static_cast<float> (this->scalar);
607 }
608
609 template <typename T>
610 octave_value
as_int8(void) const611 octave_base_int_scalar<T>::as_int8 (void) const
612 {
613 return octave_int8 (this->scalar);
614 }
615
616 template <typename T>
617 octave_value
as_int16(void) const618 octave_base_int_scalar<T>::as_int16 (void) const
619 {
620 return octave_int16 (this->scalar);
621 }
622
623 template <typename T>
624 octave_value
as_int32(void) const625 octave_base_int_scalar<T>::as_int32 (void) const
626 {
627 return octave_int32 (this->scalar);
628 }
629
630 template <typename T>
631 octave_value
as_int64(void) const632 octave_base_int_scalar<T>::as_int64 (void) const
633 {
634 return octave_int64 (this->scalar);
635 }
636
637 template <typename T>
638 octave_value
as_uint8(void) const639 octave_base_int_scalar<T>::as_uint8 (void) const
640 {
641 return octave_uint8 (this->scalar);
642 }
643
644 template <typename T>
645 octave_value
as_uint16(void) const646 octave_base_int_scalar<T>::as_uint16 (void) const
647 {
648 return octave_uint16 (this->scalar);
649 }
650
651 template <typename T>
652 octave_value
as_uint32(void) const653 octave_base_int_scalar<T>::as_uint32 (void) const
654 {
655 return octave_uint32 (this->scalar);
656 }
657
658 template <typename T>
659 octave_value
as_uint64(void) const660 octave_base_int_scalar<T>::as_uint64 (void) const
661 {
662 return octave_uint64 (this->scalar);
663 }
664
665 template <typename ST>
666 std::string
edit_display(const float_display_format & fmt,octave_idx_type,octave_idx_type) const667 octave_base_int_scalar<ST>::edit_display (const float_display_format& fmt,
668 octave_idx_type,
669 octave_idx_type) const
670 {
671 std::ostringstream buf;
672 octave_print_internal (buf, fmt, this->scalar);
673 return buf.str ();
674 }
675
676 template <typename T>
677 bool
save_ascii(std::ostream & os)678 octave_base_int_scalar<T>::save_ascii (std::ostream& os)
679 {
680 os << this->scalar << "\n";
681 return true;
682 }
683
684 template <typename T>
685 bool
load_ascii(std::istream & is)686 octave_base_int_scalar<T>::load_ascii (std::istream& is)
687 {
688 is >> this->scalar;
689 if (! is)
690 error ("load: failed to load scalar constant");
691
692 return true;
693 }
694
695 template <typename T>
696 bool
save_binary(std::ostream & os,bool)697 octave_base_int_scalar<T>::save_binary (std::ostream& os, bool)
698 {
699 os.write (reinterpret_cast<char *> (&(this->scalar)), this->byte_size ());
700 return true;
701 }
702
703 template <typename T>
704 bool
load_binary(std::istream & is,bool swap,octave::mach_info::float_format)705 octave_base_int_scalar<T>::load_binary (std::istream& is, bool swap,
706 octave::mach_info::float_format)
707 {
708 T tmp;
709
710 if (! is.read (reinterpret_cast<char *> (&tmp), this->byte_size ()))
711 return false;
712
713 if (swap)
714 swap_bytes<sizeof (T)> (&tmp);
715
716 this->scalar = tmp;
717
718 return true;
719 }
720
721 template <typename T>
722 bool
save_hdf5_internal(octave_hdf5_id loc_id,octave_hdf5_id save_type,const char * name,bool)723 octave_base_int_scalar<T>::save_hdf5_internal (octave_hdf5_id loc_id,
724 octave_hdf5_id save_type,
725 const char *name, bool)
726 {
727 bool retval = false;
728
729 #if defined (HAVE_HDF5)
730
731 hid_t save_type_hid = save_type;
732 hsize_t dimens[3];
733 hid_t space_hid, data_hid;
734 space_hid = data_hid = -1;
735
736 space_hid = H5Screate_simple (0, dimens, nullptr);
737 if (space_hid < 0) return false;
738
739 #if defined (HAVE_HDF5_18)
740 data_hid = H5Dcreate (loc_id, name, save_type_hid, space_hid,
741 octave_H5P_DEFAULT, octave_H5P_DEFAULT, octave_H5P_DEFAULT);
742 #else
743 data_hid = H5Dcreate (loc_id, name, save_type_hid, space_hid,
744 octave_H5P_DEFAULT);
745 #endif
746 if (data_hid < 0)
747 {
748 H5Sclose (space_hid);
749 return false;
750 }
751
752 retval = H5Dwrite (data_hid, save_type_hid, octave_H5S_ALL, octave_H5S_ALL,
753 octave_H5P_DEFAULT, &(this->scalar)) >= 0;
754
755 H5Dclose (data_hid);
756 H5Sclose (space_hid);
757
758 #else
759 octave_unused_parameter (loc_id);
760 octave_unused_parameter (save_type);
761 octave_unused_parameter (name);
762
763 this->warn_save ("hdf5");
764 #endif
765
766 return retval;
767 }
768
769 template <typename T>
770 bool
load_hdf5_internal(octave_hdf5_id loc_id,octave_hdf5_id save_type,const char * name)771 octave_base_int_scalar<T>::load_hdf5_internal (octave_hdf5_id loc_id,
772 octave_hdf5_id save_type,
773 const char *name)
774 {
775 #if defined (HAVE_HDF5)
776
777 hid_t save_type_hid = save_type;
778 #if defined (HAVE_HDF5_18)
779 hid_t data_hid = H5Dopen (loc_id, name, octave_H5P_DEFAULT);
780 #else
781 hid_t data_hid = H5Dopen (loc_id, name);
782 #endif
783 hid_t space_id = H5Dget_space (data_hid);
784
785 hsize_t rank = H5Sget_simple_extent_ndims (space_id);
786
787 if (rank != 0)
788 {
789 H5Dclose (data_hid);
790 return false;
791 }
792
793 T tmp;
794 if (H5Dread (data_hid, save_type_hid, octave_H5S_ALL, octave_H5S_ALL,
795 octave_H5P_DEFAULT, &tmp) < 0)
796 {
797 H5Dclose (data_hid);
798 return false;
799 }
800
801 this->scalar = tmp;
802
803 H5Dclose (data_hid);
804
805 return true;
806
807 #else
808 octave_unused_parameter (loc_id);
809 octave_unused_parameter (save_type);
810 octave_unused_parameter (name);
811
812 this->warn_load ("hdf5");
813
814 return false;
815 #endif
816 }
817