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