GNU Octave  3.8.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
typecast.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 2007-2013 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 the
10 Free Software Foundation; either version 3 of the License, or (at your
11 option) any later version.
12 
13 Octave is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 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 <http://www.gnu.org/licenses/>.
21 
22 */
23 
24 #ifdef 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 "gripes.h"
35 #include "oct-obj.h"
36 #include "unwind-prot.h"
37 
38 static dim_vector
40 {
41  if (old_dims.length () == 2 && old_dims(0) == 1)
42  return dim_vector (1, n);
43  else if (old_dims.length () == 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 <class 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,
55  unwind_protect& frame)
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 <class 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  {
77  ArrayType retval (get_vec_dims (old_dims, n));
78  T *dest = retval.fortran_vec ();
79  std::memcpy (dest, data, n * sizeof (T));
80 
81  return retval;
82  }
83  else
84  {
85  error ("typecast: incorrect number of input values to make output value");
86  return ArrayType ();
87  }
88 }
89 
90 
91 DEFUN (typecast, args, ,
92  "-*- texinfo -*-\n\
93 @deftypefn {Built-in Function} {} typecast (@var{x}, @var{class})\n\
94 Return a new array @var{y} resulting from interpreting the data of\n\
95 @var{x} in memory as data of the numeric class @var{class}. Both the class\n\
96 of @var{x} and @var{class} must be one of the built-in numeric classes:\n\
97 \n\
98 @example\n\
99 @group\n\
100 \"logical\"\n\
101 \"char\"\n\
102 \"int8\"\n\
103 \"int16\"\n\
104 \"int32\"\n\
105 \"int64\"\n\
106 \"uint8\"\n\
107 \"uint16\"\n\
108 \"uint32\"\n\
109 \"uint64\"\n\
110 \"double\"\n\
111 \"single\"\n\
112 \"double complex\"\n\
113 \"single complex\"\n\
114 @end group\n\
115 @end example\n\
116 \n\
117 @noindent\n\
118 the last two are reserved for @var{class}; they indicate that a\n\
119 complex-valued result is requested. Complex arrays are stored in memory as\n\
120 consecutive pairs of real numbers. The sizes of integer types are given by\n\
121 their bit counts. Both logical and char are typically one byte wide;\n\
122 however, this is not guaranteed by C++. If your system is IEEE conformant,\n\
123 single and double should be 4 bytes and 8 bytes wide, respectively.\n\
124 @qcode{\"logical\"} is not allowed for @var{class}. If the input is a row\n\
125 vector, the return value is a row vector, otherwise it is a column vector. \n\
126 If the bit length of @var{x} is not divisible by that of @var{class}, an\n\
127 error occurs.\n\
128 \n\
129 An example of the use of typecast on a little-endian machine is\n\
130 \n\
131 @example\n\
132 @group\n\
133 @var{x} = uint16 ([1, 65535]);\n\
134 typecast (@var{x}, \"uint8\")\n\
135 @result{} [ 1, 0, 255, 255]\n\
136 @end group\n\
137 @end example\n\
138 @seealso{cast, bitunpack, bitpack, swapbytes}\n\
139 @end deftypefn")
140 {
141  octave_value retval;
142 
143  if (args.length () == 2)
144  {
145  unwind_protect frame;
146  const void *data = 0;
147  octave_idx_type byte_size = 0;
148  dim_vector old_dims;
149 
150  octave_value array = args(0);
151 
152  if (array.is_bool_type ())
153  get_data_and_bytesize (array.bool_array_value (), data, byte_size,
154  old_dims, frame);
155  else if (array.is_string ())
156  get_data_and_bytesize (array.char_array_value (), data, byte_size,
157  old_dims, frame);
158  else if (array.is_integer_type ())
159  {
160  if (array.is_int8_type ())
161  get_data_and_bytesize (array.int8_array_value (), data, byte_size,
162  old_dims, frame);
163  else if (array.is_int16_type ())
164  get_data_and_bytesize (array.int16_array_value (), data, byte_size,
165  old_dims, frame);
166  else if (array.is_int32_type ())
167  get_data_and_bytesize (array.int32_array_value (), data, byte_size,
168  old_dims, frame);
169  else if (array.is_int64_type ())
170  get_data_and_bytesize (array.int64_array_value (), data, byte_size,
171  old_dims, frame);
172  else if (array.is_uint8_type ())
173  get_data_and_bytesize (array.uint8_array_value (), data, byte_size,
174  old_dims, frame);
175  else if (array.is_uint16_type ())
176  get_data_and_bytesize (array.uint16_array_value (), data, byte_size,
177  old_dims, frame);
178  else if (array.is_uint32_type ())
179  get_data_and_bytesize (array.uint32_array_value (), data, byte_size,
180  old_dims, frame);
181  else if (array.is_uint64_type ())
182  get_data_and_bytesize (array.uint64_array_value (), data, byte_size,
183  old_dims, frame);
184  else
185  assert (0);
186  }
187  else if (array.is_complex_type ())
188  {
189  if (array.is_single_type ())
191  byte_size, old_dims, frame);
192  else
194  byte_size, old_dims, frame);
195  }
196  else if (array.is_real_type ())
197  {
198  if (array.is_single_type ())
199  get_data_and_bytesize (array.float_array_value (), data, byte_size,
200  old_dims, frame);
201  else
202  get_data_and_bytesize (array.array_value (), data, byte_size,
203  old_dims, frame); }
204  else
205  error ("typecast: invalid input class: %s",
206  array.class_name ().c_str ());
207 
208  std::string numclass = args(1).string_value ();
209 
210  if (error_state || numclass.size () == 0)
211  ;
212  else if (numclass == "char")
213  retval = octave_value (reinterpret_copy<charNDArray>
214  (data, byte_size, old_dims), array.is_dq_string () ? '"'
215  : '\'');
216  else if (numclass[0] == 'i')
217  {
218  if (numclass == "int8")
219  retval = reinterpret_copy<int8NDArray> (data, byte_size, old_dims);
220  else if (numclass == "int16")
221  retval = reinterpret_copy<int16NDArray> (data, byte_size, old_dims);
222  else if (numclass == "int32")
223  retval = reinterpret_copy<int32NDArray> (data, byte_size, old_dims);
224  else if (numclass == "int64")
225  retval = reinterpret_copy<int64NDArray> (data, byte_size, old_dims);
226  }
227  else if (numclass[0] == 'u')
228  {
229  if (numclass == "uint8")
230  retval = reinterpret_copy<uint8NDArray> (data, byte_size, old_dims);
231  else if (numclass == "uint16")
232  retval = reinterpret_copy<uint16NDArray> (data, byte_size,
233  old_dims);
234  else if (numclass == "uint32")
235  retval = reinterpret_copy<uint32NDArray> (data, byte_size,
236  old_dims);
237  else if (numclass == "uint64")
238  retval = reinterpret_copy<uint64NDArray> (data, byte_size,
239  old_dims);
240  }
241  else if (numclass == "single")
242  retval = reinterpret_copy<FloatNDArray> (data, byte_size, old_dims);
243  else if (numclass == "double")
244  retval = reinterpret_copy<NDArray> (data, byte_size, old_dims);
245  else if (numclass == "single complex")
246  retval = reinterpret_copy<FloatComplexNDArray> (data, byte_size,
247  old_dims);
248  else if (numclass == "double complex")
249  retval = reinterpret_copy<ComplexNDArray> (data, byte_size, old_dims);
250 
251  if (! error_state && retval.is_undefined ())
252  error ("typecast: cannot convert to %s class", numclass.c_str ());
253  }
254  else
255  print_usage ();
256 
257  return retval;
258 }
259 
260 template <class ArrayType>
261 ArrayType
262 do_bitpack (const boolNDArray& bitp)
263 {
264  typedef typename ArrayType::element_type T;
266  = bitp.numel () / (sizeof (T) * std::numeric_limits<unsigned char>::digits);
267 
268  if (n * static_cast<int> (sizeof (T)) * std::numeric_limits<unsigned char>::digits == bitp.numel ())
269  {
270 
271  ArrayType retval (get_vec_dims (bitp.dims (), n));
272 
273  const bool *bits = bitp.fortran_vec ();
274  char *packed = reinterpret_cast<char *> (retval.fortran_vec ());
275 
276  octave_idx_type m = n * sizeof (T);
277 
278  for (octave_idx_type i = 0; i < m; i++)
279  {
280  char c = bits[0];
281  for (int j = 1; j < std::numeric_limits<unsigned char>::digits; j++)
282  c |= bits[j] << j;
283 
284  packed[i] = c;
285  bits += std::numeric_limits<unsigned char>::digits;
286  }
287 
288  return retval;
289  }
290  else
291  {
292  error ("bitpack: incorrect number of bits to make up output value");
293  return ArrayType ();
294  }
295 }
296 
297 DEFUN (bitpack, args, ,
298  "-*- texinfo -*-\n\
299 @deftypefn {Built-in Function} {@var{y} =} bitpack (@var{x}, @var{class})\n\
300 Return a new array @var{y} resulting from interpreting an array\n\
301 @var{x} as raw bit patterns for data of the numeric class @var{class}.\n\
302 @var{class} must be one of the built-in numeric classes:\n\
303 \n\
304 @example\n\
305 @group\n\
306 \"char\"\n\
307 \"int8\"\n\
308 \"int16\"\n\
309 \"int32\"\n\
310 \"int64\"\n\
311 \"uint8\"\n\
312 \"uint16\"\n\
313 \"uint32\"\n\
314 \"uint64\"\n\
315 \"double\"\n\
316 \"single\"\n\
317 @end group\n\
318 @end example\n\
319 \n\
320 The number of elements of @var{x} should be divisible by the bit length of\n\
321 @var{class}. If it is not, excess bits are discarded. Bits come in\n\
322 increasing order of significance, i.e., @code{x(1)} is bit 0, @code{x(2)} is\n\
323 bit 1, etc. The result is a row vector if @var{x} is a row vector, otherwise\n\
324 it is a column vector.\n\
325 @seealso{bitunpack, typecast}\n\
326 @end deftypefn")
327 {
328  octave_value retval;
329 
330  if (args.length () == 2 && args(0).is_bool_type ())
331  {
332  boolNDArray bitp = args(0).bool_array_value ();
333 
334  std::string numclass = args(1).string_value ();
335 
336  if (error_state || numclass.size () == 0)
337  ;
338  else if (numclass == "char")
339  retval = octave_value (do_bitpack<charNDArray> (bitp), '\'');
340  else if (numclass[0] == 'i')
341  {
342  if (numclass == "int8")
343  retval = do_bitpack<int8NDArray> (bitp);
344  else if (numclass == "int16")
345  retval = do_bitpack<int16NDArray> (bitp);
346  else if (numclass == "int32")
347  retval = do_bitpack<int32NDArray> (bitp);
348  else if (numclass == "int64")
349  retval = do_bitpack<int64NDArray> (bitp);
350  }
351  else if (numclass[0] == 'u')
352  {
353  if (numclass == "uint8")
354  retval = do_bitpack<uint8NDArray> (bitp);
355  else if (numclass == "uint16")
356  retval = do_bitpack<uint16NDArray> (bitp);
357  else if (numclass == "uint32")
358  retval = do_bitpack<uint32NDArray> (bitp);
359  else if (numclass == "uint64")
360  retval = do_bitpack<uint64NDArray> (bitp);
361  }
362  else if (numclass == "single")
363  retval = do_bitpack<FloatNDArray> (bitp);
364  else if (numclass == "double")
365  retval = do_bitpack<NDArray> (bitp);
366  else if (numclass == "single complex")
367  retval = do_bitpack<FloatComplexNDArray> (bitp);
368  else if (numclass == "double complex")
369  retval = do_bitpack<ComplexNDArray> (bitp);
370 
371  if (! error_state && retval.is_undefined ())
372  error ("bitpack: cannot pack to %s class", numclass.c_str ());
373  }
374  else
375  print_usage ();
376 
377  return retval;
378 }
379 
380 template <class ArrayType>
382 do_bitunpack (const ArrayType& array)
383 {
384  typedef typename ArrayType::element_type T;
385  octave_idx_type n = array.numel () * sizeof (T)
386  * std::numeric_limits<unsigned char>::digits;
387 
388  boolNDArray retval (get_vec_dims (array.dims (), n));
389 
390  const char *packed = reinterpret_cast<const char *> (array.fortran_vec ());
391  bool *bits = retval.fortran_vec ();
392 
393  octave_idx_type m = n / std::numeric_limits<unsigned char>::digits;
394 
395  for (octave_idx_type i = 0; i < m; i++)
396  {
397  char c = packed[i];
398  bits[0] = c & 1;
399  for (int j = 1; j < std::numeric_limits<unsigned char>::digits; j++)
400  bits[j] = (c >>= 1) & 1;
401  bits += std::numeric_limits<unsigned char>::digits;
402  }
403 
404  return retval;
405 }
406 
407 DEFUN (bitunpack, args, ,
408  "-*- texinfo -*-\n\
409 @deftypefn {Built-in Function} {@var{y} =} bitunpack (@var{x})\n\
410 Return an array @var{y} corresponding to the raw bit patterns of\n\
411 @var{x}. @var{x} must belong to one of the built-in numeric classes:\n\
412 \n\
413 @example\n\
414 @group\n\
415 \"char\"\n\
416 \"int8\"\n\
417 \"int16\"\n\
418 \"int32\"\n\
419 \"int64\"\n\
420 \"uint8\"\n\
421 \"uint16\"\n\
422 \"uint32\"\n\
423 \"uint64\"\n\
424 \"double\"\n\
425 \"single\"\n\
426 @end group\n\
427 @end example\n\
428 \n\
429 The result is a row vector if @var{x} is a row vector; otherwise, it is a\n\
430 column vector.\n\
431 @seealso{bitpack, typecast}\n\
432 @end deftypefn")
433 {
434  octave_value retval;
435 
436  if (args.length () == 1
437  && (args(0).is_numeric_type () || args(0).is_string ()))
438  {
439  octave_value array = args(0);
440 
441  if (array.is_string ())
442  retval = do_bitunpack (array.char_array_value ());
443  else if (array.is_integer_type ())
444  {
445  if (array.is_int8_type ())
446  retval = do_bitunpack (array.int8_array_value ());
447  else if (array.is_int16_type ())
448  retval = do_bitunpack (array.int16_array_value ());
449  else if (array.is_int32_type ())
450  retval = do_bitunpack (array.int32_array_value ());
451  else if (array.is_int64_type ())
452  retval = do_bitunpack (array.int64_array_value ());
453  else if (array.is_uint8_type ())
454  retval = do_bitunpack (array.uint8_array_value ());
455  else if (array.is_uint16_type ())
456  retval = do_bitunpack (array.uint16_array_value ());
457  else if (array.is_uint32_type ())
458  retval = do_bitunpack (array.uint32_array_value ());
459  else if (array.is_uint64_type ())
460  retval = do_bitunpack (array.uint64_array_value ());
461  else
462  assert (0);
463  }
464  else if (array.is_complex_type ())
465  {
466  if (array.is_single_type ())
467  retval = do_bitunpack (array.float_complex_array_value ());
468  else
469  retval = do_bitunpack (array.complex_array_value ());
470  }
471  else if (array.is_real_type ())
472  {
473  if (array.is_single_type ())
474  retval = do_bitunpack (array.float_array_value ());
475  else
476  retval = do_bitunpack (array.array_value ());
477  }
478  else
479  error ("bitunpack: invalid input class: %s",
480  array.class_name ().c_str ());
481  }
482  else
483  print_usage ();
484 
485  return retval;
486 }