GNU Octave 10.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
typecast.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 2007-2025 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
42
43static dim_vector
44get_vec_dims (const dim_vector& old_dims, octave_idx_type n)
45{
46 if (old_dims.ndims () == 2 && old_dims(0) == 1)
47 return dim_vector (1, n);
48 else if (old_dims.ndims () == 2 && old_dims(0) == 0 && old_dims(1) == 0)
49 return dim_vector ();
50 else
51 return dim_vector (n, 1);
52}
53
54template <typename ArrayType>
55static void
56get_data_and_bytesize (const ArrayType& array,
57 const void *&data,
58 octave_idx_type& byte_size,
59 dim_vector& old_dims,
60 unwind_protect& frame)
61{
62 // The array given may be a temporary, constructed from a scalar or sparse
63 // array. This will ensure the data will be deallocated after we exit.
64 frame.add_delete (new ArrayType (array));
65
66 data = reinterpret_cast<const void *> (array.data ());
67 byte_size = array.byte_size ();
68
69 old_dims = array.dims ();
70}
71
72template <typename ArrayType>
73static ArrayType
74reinterpret_copy (const void *data, octave_idx_type byte_size,
75 const dim_vector& old_dims)
76{
77 typedef typename ArrayType::element_type T;
78 octave_idx_type n = byte_size / sizeof (T);
79
80 if (n * static_cast<int> (sizeof (T)) != byte_size)
81 error ("typecast: incorrect number of input values to make output value");
82
83 ArrayType retval (get_vec_dims (old_dims, n));
84 T *dest = retval.rwdata ();
85 std::memcpy (dest, data, n * sizeof (T));
86
87 return retval;
88}
89
90template <typename ArrayType>
91static ArrayType
92reinterpret_int_copy (const void *data, octave_idx_type byte_size,
93 const dim_vector& old_dims)
94{
95 typedef typename ArrayType::element_type T;
96 typedef typename T::val_type VT;
97 octave_idx_type n = byte_size / sizeof (T);
98
99 if (n * static_cast<int> (sizeof (T)) != byte_size)
100 error ("typecast: incorrect number of input values to make output value");
101
102 ArrayType retval (get_vec_dims (old_dims, n));
103 VT *dest = reinterpret_cast<VT *> (retval.rwdata ());
104 std::memcpy (dest, data, n * sizeof (VT));
105
106 return retval;
107}
108
109DEFUN (typecast, args, ,
110 doc: /* -*- texinfo -*-
111@deftypefn {} {@var{y} =} typecast (@var{x}, "@var{class}")
112Return a new array @var{y} resulting from interpreting the data of @var{x}
113in memory as data of the numeric class @var{class}.
114
115Both the class of @var{x} and @var{class} must be one of the built-in
116numeric classes:
117
118@example
119@group
120"logical"
121"char"
122"int8"
123"int16"
124"int32"
125"int64"
126"uint8"
127"uint16"
128"uint32"
129"uint64"
130"double"
131"single"
132"double complex"
133"single complex"
134@end group
135@end example
136
137@noindent
138the last two are only used with @var{class}; they indicate that a
139complex-valued result is requested. Complex arrays are stored in memory as
140consecutive pairs of real numbers. The sizes of integer types are given by
141their bit counts. Both logical and char are typically one byte wide;
142however, this is not guaranteed by C++. If your system is IEEE conformant,
143single and double will be 4 bytes and 8 bytes wide, respectively.
144@qcode{"logical"} is not allowed for @var{class}.
145
146If the input is a row vector, the return value is a row vector, otherwise it
147is a column vector.
148
149If the bit length of @var{x} is not divisible by that of @var{class}, an
150error occurs.
151
152An example of the use of typecast on a little-endian machine is
153
154@example
155@group
156@var{x} = uint16 ([1, 65535]);
157typecast (@var{x}, "uint8")
158@result{} [ 1, 0, 255, 255]
159@end group
160@end example
161@seealso{cast, bitpack, bitunpack, swapbytes}
162@end deftypefn */)
163{
164 if (args.length () != 2)
165 print_usage ();
166
167 octave_value retval;
168
169 unwind_protect frame;
170
171 const void *data = nullptr;
172 octave_idx_type byte_size = 0;
173 dim_vector old_dims;
174
175 octave_value array = args(0);
176
177 if (array.islogical ())
178 get_data_and_bytesize (array.bool_array_value (), data, byte_size,
179 old_dims, frame);
180 else if (array.is_string ())
181 get_data_and_bytesize (array.char_array_value (), data, byte_size,
182 old_dims, frame);
183 else if (array.isinteger ())
184 {
185 if (array.is_int8_type ())
186 get_data_and_bytesize (array.int8_array_value (), data, byte_size,
187 old_dims, frame);
188 else if (array.is_int16_type ())
189 get_data_and_bytesize (array.int16_array_value (), data, byte_size,
190 old_dims, frame);
191 else if (array.is_int32_type ())
192 get_data_and_bytesize (array.int32_array_value (), data, byte_size,
193 old_dims, frame);
194 else if (array.is_int64_type ())
195 get_data_and_bytesize (array.int64_array_value (), data, byte_size,
196 old_dims, frame);
197 else if (array.is_uint8_type ())
198 get_data_and_bytesize (array.uint8_array_value (), data, byte_size,
199 old_dims, frame);
200 else if (array.is_uint16_type ())
201 get_data_and_bytesize (array.uint16_array_value (), data, byte_size,
202 old_dims, frame);
203 else if (array.is_uint32_type ())
204 get_data_and_bytesize (array.uint32_array_value (), data, byte_size,
205 old_dims, frame);
206 else if (array.is_uint64_type ())
207 get_data_and_bytesize (array.uint64_array_value (), data, byte_size,
208 old_dims, frame);
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),
241 array.is_dq_string () ? '"' : '\'');
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
311template <typename ArrayType>
312ArrayType
314{
315 typedef typename ArrayType::element_type T;
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.data ();
326 char *packed = reinterpret_cast<char *> (retval.rwdata ());
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
343DEFUN (bitpack, args, ,
344 doc: /* -*- texinfo -*-
345@deftypefn {} {@var{y} =} bitpack (@var{x}, @var{class})
346Return 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
369The 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
371increasing order of significance, i.e., @code{x(1)} is bit 0, @code{x(2)} is
372bit 1, etc.
373
374The result is a row vector if @var{x} is a row vector, otherwise it is a
375column 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
463template <typename ArrayType>
465do_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.data ());
474 bool *bits = retval.rwdata ();
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
490DEFUN (bitunpack, args, ,
491 doc: /* -*- texinfo -*-
492@deftypefn {} {@var{y} =} bitunpack (@var{x})
493Return 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
514The result is a row vector if @var{x} is a row vector; otherwise, it is a
515column 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 }
550 else if (array.iscomplex ())
551 {
552 if (array.is_single_type ())
553 retval = do_bitunpack (array.float_complex_array_value ());
554 else
555 retval = do_bitunpack (array.complex_array_value ());
556 }
557 else if (array.isreal ())
558 {
559 if (array.is_single_type ())
560 retval = do_bitunpack (array.float_array_value ());
561 else
562 retval = do_bitunpack (array.array_value ());
563 }
564 else
565 error ("bitunpack: invalid input class: %s",
566 array.class_name ().c_str ());
567
568 return retval;
569}
570
571/*
572%!assert (bitunpack ("\0"), zeros (1, 8, "logical"))
573%!assert (bitunpack (int8 (0)), zeros (1, 8, "logical"))
574%!assert (bitunpack (uint8 (0)), zeros (1, 8, "logical"))
575%!assert (bitunpack (int16 (0)), zeros (1, 16, "logical"))
576%!assert (bitunpack (uint16 (0)), zeros (1, 16, "logical"))
577%!assert (bitunpack (int32 (0)), zeros (1, 32, "logical"))
578%!assert (bitunpack (uint32 (0)), zeros (1, 32, "logical"))
579%!assert (bitunpack (int64 (0)), zeros (1, 64, "logical"))
580%!assert (bitunpack (uint64 (0)), zeros (1, 64, "logical"))
581%!assert (bitunpack (single (0)), zeros (1, 32, "logical"))
582%!assert (bitunpack (double (0)), zeros (1, 64, "logical"))
583%!assert (bitunpack (complex (single (0))), zeros (1, 64, "logical"))
584%!assert (bitunpack (complex (double (0))), zeros (1, 128, "logical"))
585
586%!error bitunpack ()
587%!error bitunpack (1, 2)
588%!error bitunpack ({})
589*/
590
591OCTAVE_END_NAMESPACE(octave)
const dim_vector & dims() const
Return a const-reference so that dims ()(i) works efficiently.
Definition Array.h:507
const T * data() const
Size of the specified dimension.
Definition Array.h:665
T * rwdata()
Size of the specified dimension.
octave_idx_type numel() const
Number of elements in the array.
Definition Array.h:418
void add_delete(T *obj)
Vector representing the dimensions (size) of an Array.
Definition dim-vector.h:90
octave_idx_type ndims() const
Number of dimensions.
Definition dim-vector.h:253
bool is_undefined() const
Definition ov.h:595
bool is_uint32_type() const
Definition ov.h:724
bool isinteger() const
Definition ov.h:730
boolNDArray bool_array_value(bool warn=false) const
Definition ov.h:900
std::string class_name() const
Definition ov.h:1362
int32NDArray int32_array_value() const
Definition ov.h:965
uint16NDArray uint16_array_value() const
Definition ov.h:974
int16NDArray int16_array_value() const
Definition ov.h:962
bool is_dq_string() const
Definition ov.h:643
int8NDArray int8_array_value() const
Definition ov.h:959
bool isreal() const
Definition ov.h:738
bool is_string() const
Definition ov.h:637
bool is_int8_type() const
Definition ov.h:706
ComplexNDArray complex_array_value(bool frc_str_conv=false) const
Definition ov.h:884
bool is_single_type() const
Definition ov.h:698
charNDArray char_array_value(bool frc_str_conv=false) const
Definition ov.h:906
bool is_uint8_type() const
Definition ov.h:718
bool is_uint64_type() const
Definition ov.h:727
bool is_uint16_type() const
Definition ov.h:721
uint64NDArray uint64_array_value() const
Definition ov.h:980
bool is_int16_type() const
Definition ov.h:709
bool is_int64_type() const
Definition ov.h:715
bool iscomplex() const
Definition ov.h:741
int64NDArray int64_array_value() const
Definition ov.h:968
NDArray array_value(bool frc_str_conv=false) const
Definition ov.h:865
bool is_int32_type() const
Definition ov.h:712
uint8NDArray uint8_array_value() const
Definition ov.h:971
FloatComplexNDArray float_complex_array_value(bool frc_str_conv=false) const
Definition ov.h:888
FloatNDArray float_array_value(bool frc_str_conv=false) const
Definition ov.h:868
uint32NDArray uint32_array_value() const
Definition ov.h:977
bool islogical() const
Definition ov.h:735
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
void print_usage()
Definition defun-int.h:72
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition defun.h:56
void error(const char *fmt,...)
Definition error.cc:1003
ArrayType do_bitpack(const boolNDArray &bitp)
Definition typecast.cc:313
boolNDArray do_bitunpack(const ArrayType &array)
Definition typecast.cc:465