1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2007-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 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29
30 #include <algorithm>
31 #include <limits>
32
33 #include "mx-base.h"
34
35 #include "defun.h"
36 #include "error.h"
37 #include "errwarn.h"
38 #include "ovl.h"
39 #include "unwind-prot.h"
40
41 static dim_vector
get_vec_dims(const dim_vector & old_dims,octave_idx_type n)42 get_vec_dims (const dim_vector& old_dims, octave_idx_type n)
43 {
44 if (old_dims.ndims () == 2 && old_dims(0) == 1)
45 return dim_vector (1, n);
46 else if (old_dims.ndims () == 2 && old_dims(0) == 0 && old_dims(1) == 0)
47 return dim_vector ();
48 else
49 return dim_vector (n, 1);
50 }
51
52 template <typename ArrayType>
53 static void
get_data_and_bytesize(const ArrayType & array,const void * & data,octave_idx_type & byte_size,dim_vector & old_dims,octave::unwind_protect & frame)54 get_data_and_bytesize (const ArrayType& array,
55 const void *& data,
56 octave_idx_type& byte_size,
57 dim_vector& old_dims,
58 octave::unwind_protect& frame)
59 {
60 // The array given may be a temporary, constructed from a scalar or sparse
61 // array. This will ensure the data will be deallocated after we exit.
62 frame.add_delete (new ArrayType (array));
63
64 data = reinterpret_cast<const void *> (array.data ());
65 byte_size = array.byte_size ();
66
67 old_dims = array.dims ();
68 }
69
70 template <typename ArrayType>
71 static ArrayType
reinterpret_copy(const void * data,octave_idx_type byte_size,const dim_vector & old_dims)72 reinterpret_copy (const void *data, octave_idx_type byte_size,
73 const dim_vector& old_dims)
74 {
75 typedef typename ArrayType::element_type T;
76 octave_idx_type n = byte_size / sizeof (T);
77
78 if (n * static_cast<int> (sizeof (T)) != byte_size)
79 error ("typecast: incorrect number of input values to make output value");
80
81 ArrayType retval (get_vec_dims (old_dims, n));
82 T *dest = retval.fortran_vec ();
83 std::memcpy (dest, data, n * sizeof (T));
84
85 return retval;
86 }
87
88 template <typename ArrayType>
89 static ArrayType
reinterpret_int_copy(const void * data,octave_idx_type byte_size,const dim_vector & old_dims)90 reinterpret_int_copy (const void *data, octave_idx_type byte_size,
91 const dim_vector& old_dims)
92 {
93 typedef typename ArrayType::element_type T;
94 typedef typename T::val_type VT;
95 octave_idx_type n = byte_size / sizeof (T);
96
97 if (n * static_cast<int> (sizeof (T)) != byte_size)
98 error ("typecast: incorrect number of input values to make output value");
99
100 ArrayType retval (get_vec_dims (old_dims, n));
101 VT *dest = reinterpret_cast<VT *> (retval.fortran_vec ());
102 std::memcpy (dest, data, n * sizeof (VT));
103
104 return retval;
105 }
106
107 DEFUN (typecast, args, ,
108 doc: /* -*- texinfo -*-
109 @deftypefn {} {@var{y} =} typecast (@var{x}, "@var{class}")
110 Return a new array @var{y} resulting from interpreting the data of @var{x}
111 in memory as data of the numeric class @var{class}.
112
113 Both the class of @var{x} and @var{class} must be one of the built-in
114 numeric classes:
115
116 @example
117 @group
118 "logical"
119 "char"
120 "int8"
121 "int16"
122 "int32"
123 "int64"
124 "uint8"
125 "uint16"
126 "uint32"
127 "uint64"
128 "double"
129 "single"
130 "double complex"
131 "single complex"
132 @end group
133 @end example
134
135 @noindent
136 the last two are only used with @var{class}; they indicate that a
137 complex-valued result is requested. Complex arrays are stored in memory as
138 consecutive pairs of real numbers. The sizes of integer types are given by
139 their bit counts. Both logical and char are typically one byte wide;
140 however, this is not guaranteed by C++. If your system is IEEE conformant,
141 single and double will be 4 bytes and 8 bytes wide, respectively.
142 @qcode{"logical"} is not allowed for @var{class}.
143
144 If the input is a row vector, the return value is a row vector, otherwise it
145 is a column vector.
146
147 If the bit length of @var{x} is not divisible by that of @var{class}, an
148 error occurs.
149
150 An example of the use of typecast on a little-endian machine is
151
152 @example
153 @group
154 @var{x} = uint16 ([1, 65535]);
155 typecast (@var{x}, "uint8")
156 @result{} [ 1, 0, 255, 255]
157 @end group
158 @end example
159 @seealso{cast, bitpack, bitunpack, swapbytes}
160 @end deftypefn */)
161 {
162 if (args.length () != 2)
163 print_usage ();
164
165 octave_value retval;
166
167 octave::unwind_protect frame;
168
169 const void *data = nullptr;
170 octave_idx_type byte_size = 0;
171 dim_vector old_dims;
172
173 octave_value array = args(0);
174
175 if (array.islogical ())
176 get_data_and_bytesize (array.bool_array_value (), data, byte_size,
177 old_dims, frame);
178 else if (array.is_string ())
179 get_data_and_bytesize (array.char_array_value (), data, byte_size,
180 old_dims, frame);
181 else if (array.isinteger ())
182 {
183 if (array.is_int8_type ())
184 get_data_and_bytesize (array.int8_array_value (), data, byte_size,
185 old_dims, frame);
186 else if (array.is_int16_type ())
187 get_data_and_bytesize (array.int16_array_value (), data, byte_size,
188 old_dims, frame);
189 else if (array.is_int32_type ())
190 get_data_and_bytesize (array.int32_array_value (), data, byte_size,
191 old_dims, frame);
192 else if (array.is_int64_type ())
193 get_data_and_bytesize (array.int64_array_value (), data, byte_size,
194 old_dims, frame);
195 else if (array.is_uint8_type ())
196 get_data_and_bytesize (array.uint8_array_value (), data, byte_size,
197 old_dims, frame);
198 else if (array.is_uint16_type ())
199 get_data_and_bytesize (array.uint16_array_value (), data, byte_size,
200 old_dims, frame);
201 else if (array.is_uint32_type ())
202 get_data_and_bytesize (array.uint32_array_value (), data, byte_size,
203 old_dims, frame);
204 else if (array.is_uint64_type ())
205 get_data_and_bytesize (array.uint64_array_value (), data, byte_size,
206 old_dims, frame);
207 else
208 assert (0);
209 }
210 else if (array.iscomplex ())
211 {
212 if (array.is_single_type ())
213 get_data_and_bytesize (array.float_complex_array_value (), data,
214 byte_size, old_dims, frame);
215 else
216 get_data_and_bytesize (array.complex_array_value (), data,
217 byte_size, old_dims, frame);
218 }
219 else if (array.isreal ())
220 {
221 if (array.is_single_type ())
222 get_data_and_bytesize (array.float_array_value (), data, byte_size,
223 old_dims, frame);
224 else
225 get_data_and_bytesize (array.array_value (), data, byte_size,
226 old_dims, frame);
227 }
228 else
229 error ("typecast: invalid input CLASS: %s",
230 array.class_name ().c_str ());
231
232 std::string numclass = args(1).string_value ();
233 std::transform (numclass.begin (), numclass.end (), numclass.begin (),
234 tolower);
235
236 if (numclass.size () == 0)
237 ;
238 else if (numclass == "char")
239 retval = octave_value (reinterpret_copy<charNDArray>
240 (data, byte_size, old_dims), array.is_dq_string () ? '"'
241 : '\'');
242 else if (numclass[0] == 'i')
243 {
244 if (numclass == "int8")
245 retval = reinterpret_int_copy<int8NDArray> (data, byte_size, old_dims);
246 else if (numclass == "int16")
247 retval = reinterpret_int_copy<int16NDArray> (data, byte_size, old_dims);
248 else if (numclass == "int32")
249 retval = reinterpret_int_copy<int32NDArray> (data, byte_size, old_dims);
250 else if (numclass == "int64")
251 retval = reinterpret_int_copy<int64NDArray> (data, byte_size, old_dims);
252 }
253 else if (numclass[0] == 'u')
254 {
255 if (numclass == "uint8")
256 retval = reinterpret_int_copy<uint8NDArray> (data, byte_size, old_dims);
257 else if (numclass == "uint16")
258 retval = reinterpret_int_copy<uint16NDArray> (data, byte_size,
259 old_dims);
260 else if (numclass == "uint32")
261 retval = reinterpret_int_copy<uint32NDArray> (data, byte_size,
262 old_dims);
263 else if (numclass == "uint64")
264 retval = reinterpret_int_copy<uint64NDArray> (data, byte_size,
265 old_dims);
266 }
267 else if (numclass == "single")
268 retval = reinterpret_copy<FloatNDArray> (data, byte_size, old_dims);
269 else if (numclass == "double")
270 retval = reinterpret_copy<NDArray> (data, byte_size, old_dims);
271 else if (numclass == "single complex")
272 retval = reinterpret_copy<FloatComplexNDArray> (data, byte_size,
273 old_dims);
274 else if (numclass == "double complex")
275 retval = reinterpret_copy<ComplexNDArray> (data, byte_size, old_dims);
276
277 if (retval.is_undefined ())
278 error ("typecast: cannot convert to %s class", numclass.c_str ());
279
280 return retval;
281 }
282
283 /*
284 %!assert (typecast (int64 (0), "char"), char (zeros (1, 8)))
285 %!assert (typecast (int64 (0), "int8"), zeros (1, 8, "int8"))
286 %!assert (typecast (int64 (0), "uint8"), zeros (1, 8, "uint8"))
287 %!assert (typecast (int64 (0), "int16"), zeros (1, 4, "int16"))
288 %!assert (typecast (int64 (0), "uint16"), zeros (1, 4, "uint16"))
289 %!assert (typecast (int64 (0), "int32"), zeros (1, 2, "int32"))
290 %!assert (typecast (int64 (0), "uint32"), zeros (1, 2, "uint32"))
291 %!assert (typecast (int64 (0), "int64"), zeros (1, 1, "int64"))
292 %!assert (typecast (int64 (0), "uint64"), zeros (1, 1, "uint64"))
293 %!assert (typecast (int64 (0), "single"), zeros (1, 2, "single"))
294 %!assert (typecast (int64 (0), "double"), 0)
295 %!assert (typecast (int64 (0), "single complex"), single (0))
296 %!assert (typecast (int64 ([0 0]), "double complex"), 0)
297
298 %!assert (typecast ([], "double"), [])
299 %!assert (typecast (0, "double"), 0)
300 %!assert (typecast (inf, "double"), inf)
301 %!assert (typecast (-inf, "double"), -inf)
302 %!assert (typecast (nan, "double"), nan)
303
304 %!error typecast ()
305 %!error typecast (1)
306 %!error typecast (1, 2, 3)
307 %!error typecast (1, "invalid")
308 %!error typecast (int8 (0), "double")
309 */
310
311 template <typename ArrayType>
312 ArrayType
do_bitpack(const boolNDArray & bitp)313 do_bitpack (const boolNDArray& bitp)
314 {
315 typedef typename ArrayType::element_type T;
316 octave_idx_type n
317 = bitp.numel () / (sizeof (T) * std::numeric_limits<unsigned char>::digits);
318
319 if (n * static_cast<int> (sizeof (T)) *
320 std::numeric_limits<unsigned char>::digits != bitp.numel ())
321 error ("bitpack: incorrect number of bits to make up output value");
322
323 ArrayType retval (get_vec_dims (bitp.dims (), n));
324
325 const bool *bits = bitp.fortran_vec ();
326 char *packed = reinterpret_cast<char *> (retval.fortran_vec ());
327
328 octave_idx_type m = n * sizeof (T);
329
330 for (octave_idx_type i = 0; i < m; i++)
331 {
332 char c = bits[0];
333 for (int j = 1; j < std::numeric_limits<unsigned char>::digits; j++)
334 c |= bits[j] << j;
335
336 packed[i] = c;
337 bits += std::numeric_limits<unsigned char>::digits;
338 }
339
340 return retval;
341 }
342
343 DEFUN (bitpack, args, ,
344 doc: /* -*- texinfo -*-
345 @deftypefn {} {@var{y} =} bitpack (@var{x}, @var{class})
346 Return a new array @var{y} resulting from interpreting the logical array
347 @var{x} as raw bit patterns for data of the numeric class @var{class}.
348
349 @var{class} must be one of the built-in numeric classes:
350
351 @example
352 @group
353 "double"
354 "single"
355 "double complex"
356 "single complex"
357 "char"
358 "int8"
359 "int16"
360 "int32"
361 "int64"
362 "uint8"
363 "uint16"
364 "uint32"
365 "uint64"
366 @end group
367 @end example
368
369 The number of elements of @var{x} should be divisible by the bit length of
370 @var{class}. If it is not, excess bits are discarded. Bits come in
371 increasing order of significance, i.e., @code{x(1)} is bit 0, @code{x(2)} is
372 bit 1, etc.
373
374 The result is a row vector if @var{x} is a row vector, otherwise it is a
375 column vector.
376 @seealso{bitunpack, typecast}
377 @end deftypefn */)
378 {
379 if (args.length () != 2)
380 print_usage ();
381
382 if (! args(0).islogical ())
383 error ("bitpack: X must be a logical array");
384
385 octave_value retval;
386
387 boolNDArray bitp = args(0).bool_array_value ();
388
389 std::string numclass = args(1).string_value ();
390
391 if (numclass.size () == 0)
392 ;
393 else if (numclass == "char")
394 retval = octave_value (do_bitpack<charNDArray> (bitp), '\'');
395 else if (numclass[0] == 'i')
396 {
397 if (numclass == "int8")
398 retval = do_bitpack<int8NDArray> (bitp);
399 else if (numclass == "int16")
400 retval = do_bitpack<int16NDArray> (bitp);
401 else if (numclass == "int32")
402 retval = do_bitpack<int32NDArray> (bitp);
403 else if (numclass == "int64")
404 retval = do_bitpack<int64NDArray> (bitp);
405 }
406 else if (numclass[0] == 'u')
407 {
408 if (numclass == "uint8")
409 retval = do_bitpack<uint8NDArray> (bitp);
410 else if (numclass == "uint16")
411 retval = do_bitpack<uint16NDArray> (bitp);
412 else if (numclass == "uint32")
413 retval = do_bitpack<uint32NDArray> (bitp);
414 else if (numclass == "uint64")
415 retval = do_bitpack<uint64NDArray> (bitp);
416 }
417 else if (numclass == "single")
418 retval = do_bitpack<FloatNDArray> (bitp);
419 else if (numclass == "double")
420 retval = do_bitpack<NDArray> (bitp);
421 else if (numclass == "single complex")
422 retval = do_bitpack<FloatComplexNDArray> (bitp);
423 else if (numclass == "double complex")
424 retval = do_bitpack<ComplexNDArray> (bitp);
425
426 if (retval.is_undefined ())
427 error ("bitpack: cannot pack to %s class", numclass.c_str ());
428
429 return retval;
430 }
431
432 /*
433 %!assert (bitpack (zeros (1, 8, "logical"), "char"), "\0")
434 %!assert (bitpack (zeros (1, 8, "logical"), "int8"), int8 (0))
435 %!assert (bitpack (zeros (1, 8, "logical"), "uint8"), uint8 (0))
436 %!assert (bitpack (zeros (1, 16, "logical"), "int16"), int16 (0))
437 %!assert (bitpack (zeros (1, 16, "logical"), "uint16"), uint16 (0))
438 %!assert (bitpack (zeros (1, 32, "logical"), "int32"), int32 (0))
439 %!assert (bitpack (zeros (1, 32, "logical"), "uint32"), uint32 (0))
440 %!assert (bitpack (zeros (1, 64, "logical"), "int64"), int64 (0))
441 %!assert (bitpack (zeros (1, 64, "logical"), "uint64"), uint64 (0))
442 %!assert (bitpack (zeros (1, 32, "logical"), "single"), single (0))
443 %!assert (bitpack (zeros (1, 64, "logical"), "double"), double (0))
444 %!assert (bitpack (zeros (1, 64, "logical"), "single complex"), single (0))
445 %!assert (bitpack (zeros (1, 128, "logical"), "double complex"), double (0))
446
447 %!test <54931>
448 %! x = false (1, 32);
449 %! x(1) = true;
450 %! assert (bitpack (x, "uint32"), uint32 (1));
451 %! x([1, 9]) = true;
452 %! assert (bitpack (x, "uint32"), uint32 (257));
453
454 %!error bitpack ()
455 %!error bitpack (1)
456 %!error bitpack (1, 2, 3)
457 %!error bitpack (1, "invalid")
458 %!error bitpack (1, "double")
459 %!error bitpack (false, "invalid")
460 %!error bitpack (false, "double")
461 */
462
463 template <typename ArrayType>
464 boolNDArray
do_bitunpack(const ArrayType & array)465 do_bitunpack (const ArrayType& array)
466 {
467 typedef typename ArrayType::element_type T;
468 octave_idx_type n = array.numel () * sizeof (T)
469 * std::numeric_limits<unsigned char>::digits;
470
471 boolNDArray retval (get_vec_dims (array.dims (), n));
472
473 const char *packed = reinterpret_cast<const char *> (array.fortran_vec ());
474 bool *bits = retval.fortran_vec ();
475
476 octave_idx_type m = n / std::numeric_limits<unsigned char>::digits;
477
478 for (octave_idx_type i = 0; i < m; i++)
479 {
480 char c = packed[i];
481 bits[0] = c & 1;
482 for (int j = 1; j < std::numeric_limits<unsigned char>::digits; j++)
483 bits[j] = (c >>= 1) & 1;
484 bits += std::numeric_limits<unsigned char>::digits;
485 }
486
487 return retval;
488 }
489
490 DEFUN (bitunpack, args, ,
491 doc: /* -*- texinfo -*-
492 @deftypefn {} {@var{y} =} bitunpack (@var{x})
493 Return a logical array @var{y} corresponding to the raw bit patterns of
494 @var{x}.
495
496 @var{x} must belong to one of the built-in numeric classes:
497
498 @example
499 @group
500 "double"
501 "single"
502 "char"
503 "int8"
504 "int16"
505 "int32"
506 "int64"
507 "uint8"
508 "uint16"
509 "uint32"
510 "uint64"
511 @end group
512 @end example
513
514 The result is a row vector if @var{x} is a row vector; otherwise, it is a
515 column vector.
516 @seealso{bitpack, typecast}
517 @end deftypefn */)
518 {
519 if (args.length () != 1)
520 print_usage ();
521
522 if (! (args(0).isnumeric () || args(0).is_string ()))
523 error ("bitunpack: argument must be a number or a string");
524
525 octave_value retval;
526
527 octave_value array = args(0);
528
529 if (array.is_string ())
530 retval = do_bitunpack (array.char_array_value ());
531 else if (array.isinteger ())
532 {
533 if (array.is_int8_type ())
534 retval = do_bitunpack (array.int8_array_value ());
535 else if (array.is_int16_type ())
536 retval = do_bitunpack (array.int16_array_value ());
537 else if (array.is_int32_type ())
538 retval = do_bitunpack (array.int32_array_value ());
539 else if (array.is_int64_type ())
540 retval = do_bitunpack (array.int64_array_value ());
541 else if (array.is_uint8_type ())
542 retval = do_bitunpack (array.uint8_array_value ());
543 else if (array.is_uint16_type ())
544 retval = do_bitunpack (array.uint16_array_value ());
545 else if (array.is_uint32_type ())
546 retval = do_bitunpack (array.uint32_array_value ());
547 else if (array.is_uint64_type ())
548 retval = do_bitunpack (array.uint64_array_value ());
549 else
550 assert (0);
551 }
552 else if (array.iscomplex ())
553 {
554 if (array.is_single_type ())
555 retval = do_bitunpack (array.float_complex_array_value ());
556 else
557 retval = do_bitunpack (array.complex_array_value ());
558 }
559 else if (array.isreal ())
560 {
561 if (array.is_single_type ())
562 retval = do_bitunpack (array.float_array_value ());
563 else
564 retval = do_bitunpack (array.array_value ());
565 }
566 else
567 error ("bitunpack: invalid input class: %s",
568 array.class_name ().c_str ());
569
570 return retval;
571 }
572
573 /*
574 %!assert (bitunpack ("\0"), zeros (1, 8, "logical"))
575 %!assert (bitunpack (int8 (0)), zeros (1, 8, "logical"))
576 %!assert (bitunpack (uint8 (0)), zeros (1, 8, "logical"))
577 %!assert (bitunpack (int16 (0)), zeros (1, 16, "logical"))
578 %!assert (bitunpack (uint16 (0)), zeros (1, 16, "logical"))
579 %!assert (bitunpack (int32 (0)), zeros (1, 32, "logical"))
580 %!assert (bitunpack (uint32 (0)), zeros (1, 32, "logical"))
581 %!assert (bitunpack (int64 (0)), zeros (1, 64, "logical"))
582 %!assert (bitunpack (uint64 (0)), zeros (1, 64, "logical"))
583 %!assert (bitunpack (single (0)), zeros (1, 32, "logical"))
584 %!assert (bitunpack (double (0)), zeros (1, 64, "logical"))
585 %!assert (bitunpack (complex (single (0))), zeros (1, 64, "logical"))
586 %!assert (bitunpack (complex (double (0))), zeros (1, 128, "logical"))
587
588 %!error bitunpack ()
589 %!error bitunpack (1, 2)
590 %!error bitunpack ({})
591 */
592