GNU Octave  4.4.1
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
typecast.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 2007-2018 David Bateman
4 Copyright (C) 2009 VZLU Prague
5 
6 This file is part of Octave.
7 
8 Octave is free software: you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12 
13 Octave is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with Octave; see the file COPYING. If not, see
20 <https://www.gnu.org/licenses/>.
21 
22 */
23 
24 #if defined (HAVE_CONFIG_H)
25 # include "config.h"
26 #endif
27 
28 #include <limits>
29 
30 #include "mx-base.h"
31 
32 #include "defun.h"
33 #include "error.h"
34 #include "errwarn.h"
35 #include "ovl.h"
36 #include "unwind-prot.h"
37 
38 static dim_vector
40 {
41  if (old_dims.ndims () == 2 && old_dims(0) == 1)
42  return dim_vector (1, n);
43  else if (old_dims.ndims () == 2 && old_dims(0) == 0 && old_dims(1) == 0)
44  return dim_vector ();
45  else
46  return dim_vector (n, 1);
47 }
48 
49 template <typename ArrayType>
50 static void
51 get_data_and_bytesize (const ArrayType& array,
52  const void *& data,
53  octave_idx_type& byte_size,
54  dim_vector& old_dims,
56 {
57  // The array given may be a temporary, constructed from a scalar or sparse
58  // array. This will ensure the data will be deallocated after we exit.
59  frame.add_delete (new ArrayType (array));
60 
61  data = reinterpret_cast<const void *> (array.data ());
62  byte_size = array.byte_size ();
63 
64  old_dims = array.dims ();
65 }
66 
67 template <typename ArrayType>
68 static ArrayType
69 reinterpret_copy (const void *data, octave_idx_type byte_size,
70  const dim_vector& old_dims)
71 {
72  typedef typename ArrayType::element_type T;
73  octave_idx_type n = byte_size / sizeof (T);
74 
75  if (n * static_cast<int> (sizeof (T)) != byte_size)
76  error ("typecast: incorrect number of input values to make output value");
77 
78  ArrayType retval (get_vec_dims (old_dims, n));
79  T *dest = retval.fortran_vec ();
80  std::memcpy (dest, data, n * sizeof (T));
81 
82  return retval;
83 }
84 
85 template <typename ArrayType>
86 static ArrayType
87 reinterpret_int_copy (const void *data, octave_idx_type byte_size,
88  const dim_vector& old_dims)
89 {
90  typedef typename ArrayType::element_type T;
91  typedef typename T::val_type VT;
92  octave_idx_type n = byte_size / sizeof (T);
93 
94  if (n * static_cast<int> (sizeof (T)) != byte_size)
95  error ("typecast: incorrect number of input values to make output value");
96 
97  ArrayType retval (get_vec_dims (old_dims, n));
98  VT *dest = reinterpret_cast<VT *> (retval.fortran_vec ());
99  std::memcpy (dest, data, n * sizeof (VT));
100 
101  return retval;
102 }
103 
104 DEFUN (typecast, args, ,
105  doc: /* -*- texinfo -*-
106 @deftypefn {} {@var{y} =} typecast (@var{x}, "@var{class}")
107 Return a new array @var{y} resulting from interpreting the data of @var{x}
108 in memory as data of the numeric class @var{class}.
109 
110 Both the class of @var{x} and @var{class} must be one of the built-in
111 numeric classes:
112 
113 @example
114 @group
115 "logical"
116 "char"
117 "int8"
118 "int16"
119 "int32"
120 "int64"
121 "uint8"
122 "uint16"
123 "uint32"
124 "uint64"
125 "double"
126 "single"
127 "double complex"
128 "single complex"
129 @end group
130 @end example
131 
132 @noindent
133 the last two are only used with @var{class}; they indicate that a
134 complex-valued result is requested. Complex arrays are stored in memory as
135 consecutive pairs of real numbers. The sizes of integer types are given by
136 their bit counts. Both logical and char are typically one byte wide;
137 however, this is not guaranteed by C++. If your system is IEEE conformant,
138 single and double will be 4 bytes and 8 bytes wide, respectively.
139 @qcode{"logical"} is not allowed for @var{class}.
140 
141 If the input is a row vector, the return value is a row vector, otherwise it
142 is a column vector.
143 
144 If the bit length of @var{x} is not divisible by that of @var{class}, an
145 error occurs.
146 
147 An example of the use of typecast on a little-endian machine is
148 
149 @example
150 @group
151 @var{x} = uint16 ([1, 65535]);
152 typecast (@var{x}, "uint8")
153 @result{} [ 1, 0, 255, 255]
154 @end group
155 @end example
156 @seealso{cast, bitpack, bitunpack, swapbytes}
157 @end deftypefn */)
158 {
159  if (args.length () != 2)
160  print_usage ();
161 
163 
165 
166  const void *data = nullptr;
167  octave_idx_type byte_size = 0;
168  dim_vector old_dims;
169 
170  octave_value array = args(0);
171 
172  if (array.islogical ())
173  get_data_and_bytesize (array.bool_array_value (), data, byte_size,
174  old_dims, frame);
175  else if (array.is_string ())
176  get_data_and_bytesize (array.char_array_value (), data, byte_size,
177  old_dims, frame);
178  else if (array.isinteger ())
179  {
180  if (array.is_int8_type ())
181  get_data_and_bytesize (array.int8_array_value (), data, byte_size,
182  old_dims, frame);
183  else if (array.is_int16_type ())
184  get_data_and_bytesize (array.int16_array_value (), data, byte_size,
185  old_dims, frame);
186  else if (array.is_int32_type ())
187  get_data_and_bytesize (array.int32_array_value (), data, byte_size,
188  old_dims, frame);
189  else if (array.is_int64_type ())
190  get_data_and_bytesize (array.int64_array_value (), data, byte_size,
191  old_dims, frame);
192  else if (array.is_uint8_type ())
193  get_data_and_bytesize (array.uint8_array_value (), data, byte_size,
194  old_dims, frame);
195  else if (array.is_uint16_type ())
196  get_data_and_bytesize (array.uint16_array_value (), data, byte_size,
197  old_dims, frame);
198  else if (array.is_uint32_type ())
199  get_data_and_bytesize (array.uint32_array_value (), data, byte_size,
200  old_dims, frame);
201  else if (array.is_uint64_type ())
202  get_data_and_bytesize (array.uint64_array_value (), data, byte_size,
203  old_dims, frame);
204  else
205  assert (0);
206  }
207  else if (array.iscomplex ())
208  {
209  if (array.is_single_type ())
210  get_data_and_bytesize (array.float_complex_array_value (), data,
211  byte_size, old_dims, frame);
212  else
213  get_data_and_bytesize (array.complex_array_value (), data,
214  byte_size, old_dims, frame);
215  }
216  else if (array.isreal ())
217  {
218  if (array.is_single_type ())
219  get_data_and_bytesize (array.float_array_value (), data, byte_size,
220  old_dims, frame);
221  else
222  get_data_and_bytesize (array.array_value (), data, byte_size,
223  old_dims, frame);
224  }
225  else
226  error ("typecast: invalid input CLASS: %s",
227  array.class_name ().c_str ());
228 
229  std::string numclass = args(1).string_value ();
230 
231  if (numclass.size () == 0)
232  ;
233  else if (numclass == "char")
234  retval = octave_value (reinterpret_copy<charNDArray>
235  (data, byte_size, old_dims), array.is_dq_string () ? '"'
236  : '\'');
237  else if (numclass[0] == 'i')
238  {
239  if (numclass == "int8")
240  retval = reinterpret_int_copy<int8NDArray> (data, byte_size, old_dims);
241  else if (numclass == "int16")
242  retval = reinterpret_int_copy<int16NDArray> (data, byte_size, old_dims);
243  else if (numclass == "int32")
244  retval = reinterpret_int_copy<int32NDArray> (data, byte_size, old_dims);
245  else if (numclass == "int64")
246  retval = reinterpret_int_copy<int64NDArray> (data, byte_size, old_dims);
247  }
248  else if (numclass[0] == 'u')
249  {
250  if (numclass == "uint8")
251  retval = reinterpret_int_copy<uint8NDArray> (data, byte_size, old_dims);
252  else if (numclass == "uint16")
253  retval = reinterpret_int_copy<uint16NDArray> (data, byte_size,
254  old_dims);
255  else if (numclass == "uint32")
256  retval = reinterpret_int_copy<uint32NDArray> (data, byte_size,
257  old_dims);
258  else if (numclass == "uint64")
259  retval = reinterpret_int_copy<uint64NDArray> (data, byte_size,
260  old_dims);
261  }
262  else if (numclass == "single")
263  retval = reinterpret_copy<FloatNDArray> (data, byte_size, old_dims);
264  else if (numclass == "double")
265  retval = reinterpret_copy<NDArray> (data, byte_size, old_dims);
266  else if (numclass == "single complex")
267  retval = reinterpret_copy<FloatComplexNDArray> (data, byte_size,
268  old_dims);
269  else if (numclass == "double complex")
270  retval = reinterpret_copy<ComplexNDArray> (data, byte_size, old_dims);
271 
272  if (retval.is_undefined ())
273  error ("typecast: cannot convert to %s class", numclass.c_str ());
274 
275  return retval;
276 }
277 
278 /*
279 %!assert (typecast (int64 (0), "char"), char (zeros (1, 8)))
280 %!assert (typecast (int64 (0), "int8"), zeros (1, 8, "int8"))
281 %!assert (typecast (int64 (0), "uint8"), zeros (1, 8, "uint8"))
282 %!assert (typecast (int64 (0), "int16"), zeros (1, 4, "int16"))
283 %!assert (typecast (int64 (0), "uint16"), zeros (1, 4, "uint16"))
284 %!assert (typecast (int64 (0), "int32"), zeros (1, 2, "int32"))
285 %!assert (typecast (int64 (0), "uint32"), zeros (1, 2, "uint32"))
286 %!assert (typecast (int64 (0), "int64"), zeros (1, 1, "int64"))
287 %!assert (typecast (int64 (0), "uint64"), zeros (1, 1, "uint64"))
288 %!assert (typecast (int64 (0), "single"), zeros (1, 2, "single"))
289 %!assert (typecast (int64 (0), "double"), 0)
290 %!assert (typecast (int64 (0), "single complex"), single (0))
291 %!assert (typecast (int64 ([0 0]), "double complex"), 0)
292 
293 %!assert (typecast ([], "double"), [])
294 %!assert (typecast (0, "double"), 0)
295 %!assert (typecast (inf, "double"), inf)
296 %!assert (typecast (-inf, "double"), -inf)
297 %!assert (typecast (nan, "double"), nan)
298 
299 %!error typecast ()
300 %!error typecast (1)
301 %!error typecast (1, 2, 3)
302 %!error typecast (1, "invalid")
303 %!error typecast (int8 (0), "double")
304 */
305 
306 template <typename ArrayType>
307 ArrayType
308 do_bitpack (const boolNDArray& bitp)
309 {
310  typedef typename ArrayType::element_type T;
312  = bitp.numel () / (sizeof (T) * std::numeric_limits<unsigned char>::digits);
313 
314  if (n * static_cast<int> (sizeof (T)) *
315  std::numeric_limits<unsigned char>::digits != bitp.numel ())
316  error ("bitpack: incorrect number of bits to make up output value");
317 
318  ArrayType retval (get_vec_dims (bitp.dims (), n));
319 
320  const bool *bits = bitp.fortran_vec ();
321  char *packed = reinterpret_cast<char *> (retval.fortran_vec ());
322 
323  octave_idx_type m = n * sizeof (T);
324 
325  for (octave_idx_type i = 0; i < m; i++)
326  {
327  char c = bits[0];
328  for (int j = 1; j < std::numeric_limits<unsigned char>::digits; j++)
329  c |= bits[j] << j;
330 
331  packed[i] = c;
332  bits += std::numeric_limits<unsigned char>::digits;
333  }
334 
335  return retval;
336 }
337 
338 DEFUN (bitpack, args, ,
339  doc: /* -*- texinfo -*-
340 @deftypefn {} {@var{y} =} bitpack (@var{x}, @var{class})
341 Return a new array @var{y} resulting from interpreting the logical array
342 @var{x} as raw bit patterns for data of the numeric class @var{class}.
343 
344 @var{class} must be one of the built-in numeric classes:
345 
346 @example
347 @group
348 "double"
349 "single"
350 "double complex"
351 "single complex"
352 "char"
353 "int8"
354 "int16"
355 "int32"
356 "int64"
357 "uint8"
358 "uint16"
359 "uint32"
360 "uint64"
361 @end group
362 @end example
363 
364 The number of elements of @var{x} should be divisible by the bit length of
365 @var{class}. If it is not, excess bits are discarded. Bits come in
366 increasing order of significance, i.e., @code{x(1)} is bit 0, @code{x(2)} is
367 bit 1, etc.
368 
369 The result is a row vector if @var{x} is a row vector, otherwise it is a
370 column vector.
371 @seealso{bitunpack, typecast}
372 @end deftypefn */)
373 {
374  if (args.length () != 2)
375  print_usage ();
376 
377  if (! args(0).islogical ())
378  error ("bitpack: X must be a logical array");
379 
381 
382  boolNDArray bitp = args(0).bool_array_value ();
383 
384  std::string numclass = args(1).string_value ();
385 
386  if (numclass.size () == 0)
387  ;
388  else if (numclass == "char")
389  retval = octave_value (do_bitpack<charNDArray> (bitp), '\'');
390  else if (numclass[0] == 'i')
391  {
392  if (numclass == "int8")
393  retval = do_bitpack<int8NDArray> (bitp);
394  else if (numclass == "int16")
395  retval = do_bitpack<int16NDArray> (bitp);
396  else if (numclass == "int32")
397  retval = do_bitpack<int32NDArray> (bitp);
398  else if (numclass == "int64")
399  retval = do_bitpack<int64NDArray> (bitp);
400  }
401  else if (numclass[0] == 'u')
402  {
403  if (numclass == "uint8")
404  retval = do_bitpack<uint8NDArray> (bitp);
405  else if (numclass == "uint16")
406  retval = do_bitpack<uint16NDArray> (bitp);
407  else if (numclass == "uint32")
408  retval = do_bitpack<uint32NDArray> (bitp);
409  else if (numclass == "uint64")
410  retval = do_bitpack<uint64NDArray> (bitp);
411  }
412  else if (numclass == "single")
413  retval = do_bitpack<FloatNDArray> (bitp);
414  else if (numclass == "double")
415  retval = do_bitpack<NDArray> (bitp);
416  else if (numclass == "single complex")
417  retval = do_bitpack<FloatComplexNDArray> (bitp);
418  else if (numclass == "double complex")
419  retval = do_bitpack<ComplexNDArray> (bitp);
420 
421  if (retval.is_undefined ())
422  error ("bitpack: cannot pack to %s class", numclass.c_str ());
423 
424  return retval;
425 }
426 
427 /*
428 %!assert (bitpack (zeros (1, 8, "logical"), "char"), "\0")
429 %!assert (bitpack (zeros (1, 8, "logical"), "int8"), int8 (0))
430 %!assert (bitpack (zeros (1, 8, "logical"), "uint8"), uint8 (0))
431 %!assert (bitpack (zeros (1, 16, "logical"), "int16"), int16 (0))
432 %!assert (bitpack (zeros (1, 16, "logical"), "uint16"), uint16 (0))
433 %!assert (bitpack (zeros (1, 32, "logical"), "int32"), int32 (0))
434 %!assert (bitpack (zeros (1, 32, "logical"), "uint32"), uint32 (0))
435 %!assert (bitpack (zeros (1, 64, "logical"), "int64"), int64 (0))
436 %!assert (bitpack (zeros (1, 64, "logical"), "uint64"), uint64 (0))
437 %!assert (bitpack (zeros (1, 32, "logical"), "single"), single (0))
438 %!assert (bitpack (zeros (1, 64, "logical"), "double"), double (0))
439 %!assert (bitpack (zeros (1, 64, "logical"), "single complex"), single (0))
440 %!assert (bitpack (zeros (1, 128, "logical"), "double complex"), double (0))
441 
442 %!error bitpack ()
443 %!error bitpack (1)
444 %!error bitpack (1, 2, 3)
445 %!error bitpack (1, "invalid")
446 %!error bitpack (1, "double")
447 %!error bitpack (false, "invalid")
448 %!error bitpack (false, "double")
449 */
450 
451 template <typename ArrayType>
453 do_bitunpack (const ArrayType& array)
454 {
455  typedef typename ArrayType::element_type T;
456  octave_idx_type n = array.numel () * sizeof (T)
457  * std::numeric_limits<unsigned char>::digits;
458 
459  boolNDArray retval (get_vec_dims (array.dims (), n));
460 
461  const char *packed = reinterpret_cast<const char *> (array.fortran_vec ());
462  bool *bits = retval.fortran_vec ();
463 
464  octave_idx_type m = n / std::numeric_limits<unsigned char>::digits;
465 
466  for (octave_idx_type i = 0; i < m; i++)
467  {
468  char c = packed[i];
469  bits[0] = c & 1;
470  for (int j = 1; j < std::numeric_limits<unsigned char>::digits; j++)
471  bits[j] = (c >>= 1) & 1;
472  bits += std::numeric_limits<unsigned char>::digits;
473  }
474 
475  return retval;
476 }
477 
478 DEFUN (bitunpack, args, ,
479  doc: /* -*- texinfo -*-
480 @deftypefn {} {@var{y} =} bitunpack (@var{x})
481 Return a logical array @var{y} corresponding to the raw bit patterns of
482 @var{x}.
483 
484 @var{x} must belong to one of the built-in numeric classes:
485 
486 @example
487 @group
488 "double"
489 "single"
490 "char"
491 "int8"
492 "int16"
493 "int32"
494 "int64"
495 "uint8"
496 "uint16"
497 "uint32"
498 "uint64"
499 @end group
500 @end example
501 
502 The result is a row vector if @var{x} is a row vector; otherwise, it is a
503 column vector.
504 @seealso{bitpack, typecast}
505 @end deftypefn */)
506 {
507  if (args.length () != 1)
508  print_usage ();
509 
510  if (! (args(0).isnumeric () || args(0).is_string ()))
511  error ("bitunpack: argument must be a number or a string");
512 
514 
515  octave_value array = args(0);
516 
517  if (array.is_string ())
518  retval = do_bitunpack (array.char_array_value ());
519  else if (array.isinteger ())
520  {
521  if (array.is_int8_type ())
522  retval = do_bitunpack (array.int8_array_value ());
523  else if (array.is_int16_type ())
524  retval = do_bitunpack (array.int16_array_value ());
525  else if (array.is_int32_type ())
526  retval = do_bitunpack (array.int32_array_value ());
527  else if (array.is_int64_type ())
528  retval = do_bitunpack (array.int64_array_value ());
529  else if (array.is_uint8_type ())
530  retval = do_bitunpack (array.uint8_array_value ());
531  else if (array.is_uint16_type ())
532  retval = do_bitunpack (array.uint16_array_value ());
533  else if (array.is_uint32_type ())
534  retval = do_bitunpack (array.uint32_array_value ());
535  else if (array.is_uint64_type ())
536  retval = do_bitunpack (array.uint64_array_value ());
537  else
538  assert (0);
539  }
540  else if (array.iscomplex ())
541  {
542  if (array.is_single_type ())
543  retval = do_bitunpack (array.float_complex_array_value ());
544  else
545  retval = do_bitunpack (array.complex_array_value ());
546  }
547  else if (array.isreal ())
548  {
549  if (array.is_single_type ())
550  retval = do_bitunpack (array.float_array_value ());
551  else
552  retval = do_bitunpack (array.array_value ());
553  }
554  else
555  error ("bitunpack: invalid input class: %s",
556  array.class_name ().c_str ());
557 
558  return retval;
559 }
560 
561 /*
562 %!assert (bitunpack ("\0"), zeros (1, 8, "logical"))
563 %!assert (bitunpack (int8 (0)), zeros (1, 8, "logical"))
564 %!assert (bitunpack (uint8 (0)), zeros (1, 8, "logical"))
565 %!assert (bitunpack (int16 (0)), zeros (1, 16, "logical"))
566 %!assert (bitunpack (uint16 (0)), zeros (1, 16, "logical"))
567 %!assert (bitunpack (int32 (0)), zeros (1, 32, "logical"))
568 %!assert (bitunpack (uint32 (0)), zeros (1, 32, "logical"))
569 %!assert (bitunpack (int64 (0)), zeros (1, 64, "logical"))
570 %!assert (bitunpack (uint64 (0)), zeros (1, 64, "logical"))
571 %!assert (bitunpack (single (0)), zeros (1, 32, "logical"))
572 %!assert (bitunpack (double (0)), zeros (1, 64, "logical"))
573 %!assert (bitunpack (complex (single (0))), zeros (1, 64, "logical"))
574 %!assert (bitunpack (complex (double (0))), zeros (1, 128, "logical"))
575 
576 %!error bitunpack ()
577 %!error bitunpack (1, 2)
578 %!error bitunpack ({})
579 */
typecast(ar{x}, "uint8") esult
Definition: typecast.cc:157
OCTINTERP_API void print_usage(void)
Definition: defun.cc:54
const T * fortran_vec(void) const
Definition: Array.h:584
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:53
void error(const char *fmt,...)
Definition: error.cc:578
const dim_vector & dims(void) const
Return a const-reference so that dims ()(i) works efficiently.
Definition: Array.h:442
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)
Definition: typecast.cc:51
nd example oindent opens the file binary numeric values will be read assuming they are stored in IEEE format with the least significant bit and then converted to the native representation Opening a file that is already open simply opens it again and returns a separate file id It is not an error to open a file several though writing to the same file through several different file ids may produce unexpected results The possible values of text mode reading and writing automatically converts linefeeds to the appropriate line end character for the you may append a you must also open the file in binary mode The parameter conversions are currently only supported for and permissions will be set to and then everything is written in a single operation This is very efficient and improves performance c
Definition: file-io.cc:587
OCTAVE_EXPORT octave_value_list isnumeric
Definition: data.cc:3157
then the function must return scalars which will be concatenated into the return array(s). If code
Definition: cellfun.cc:400
octave_value retval
Definition: data.cc:6246
octave::unwind_protect frame
Definition: graphics.cc:12190
return octave_value(v1.char_array_value() . concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string()) ? '\'' :'"'))
bool is_undefined(void) const
Definition: ov.h:526
static ArrayType reinterpret_copy(const void *data, octave_idx_type byte_size, const dim_vector &old_dims)
Definition: typecast.cc:69
static ArrayType reinterpret_int_copy(const void *data, octave_idx_type byte_size, const dim_vector &old_dims)
Definition: typecast.cc:87
static dim_vector get_vec_dims(const dim_vector &old_dims, octave_idx_type n)
Definition: typecast.cc:39
for i
Definition: data.cc:5264
octave_idx_type ndims(void) const
Number of dimensions.
Definition: dim-vector.h:295
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:366
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:87
If this string is the system will ring the terminal sometimes it is useful to be able to print the original representation of the string
Definition: utils.cc:888