GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
hex2num.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2008-2024 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 "defun.h"
31 #include "error.h"
32 #include "errwarn.h"
33 #include "mach-info.h"
34 #include "ov.h"
35 #include "ovl.h"
36 #include "utils.h"
37 
39 
40 static inline bool
41 is_little_endian (bool is_float)
42 {
43  return ((is_float && (mach_info::native_float_format ()
46 }
47 
48 static uint8_t
49 hex2nibble (unsigned char ch)
50 {
51  unsigned char val = 0;
52 
53  if (! isxdigit (ch))
54  error ("hex2num: invalid character '%c' found in string S", ch);
55 
56  if (ch >= 'a')
57  val = static_cast<unsigned char> (ch - 'a' + 10);
58  else if (ch >= 'A')
59  val = static_cast<unsigned char> (ch - 'A' + 10);
60  else
61  val = static_cast<unsigned char> (ch - '0');
62 
63  return val;
64 }
65 
66 static void
67 hex2num (const std::string& hex, void *num, std::size_t nbytes, bool swap_bytes)
68 {
69  unsigned char *cp = reinterpret_cast<unsigned char *> (num);
70 
71  const std::size_t nc = hex.length ();
72  const std::size_t nchars = 2 * nbytes;
73 
74  if (nc > nchars)
75  error ("hex2num: S must be no more than %zd characters", nchars);
76 
77  std::size_t j = 0;
78 
79  for (std::size_t i = 0; i < nbytes; i++)
80  {
81  std::size_t k = (swap_bytes ? nbytes - i - 1 : i);
82 
83  unsigned char ch1 = (j < nc) ? hex[j++] : '0';
84  unsigned char ch2 = (j < nc) ? hex[j++] : '0';
85 
86  cp[k] = (hex2nibble (ch1) << 4) + hex2nibble (ch2);
87  }
88 }
89 
90 template <typename T>
92 hex2num (const Array<std::string>& val, bool swap_bytes)
93 {
94  octave_idx_type nel = val.numel ();
95 
96  Array<T> m (val.dims ());
97 
98  std::size_t nbytes = sizeof (T);
99 
100  for (octave_idx_type i = 0; i < nel; i++)
101  {
102  T num;
103 
104  hex2num (val.xelem (i), &num, nbytes, swap_bytes);
105 
106  m(i) = num;
107  }
108 
109  return m;
110 }
111 
112 DEFUN (hex2num, args, ,
113  doc: /* -*- texinfo -*-
114 @deftypefn {} {@var{n} =} hex2num (@var{s})
115 @deftypefnx {} {@var{n} =} hex2num (@var{s}, @var{class})
116 Typecast a hexadecimal character array or cell array of strings to an
117 array of numbers.
118 
119 By default, the input array is interpreted as a hexadecimal number
120 representing a double precision value. If fewer than 16 characters are
121 given the strings are right padded with @qcode{'0'} characters.
122 
123 Given a string matrix, @code{hex2num} treats each row as a separate number.
124 
125 @example
126 @group
127 hex2num (["4005bf0a8b145769"; "4024000000000000"])
128  @result{} [2.7183; 10.000]
129 @end group
130 @end example
131 
132 The optional second argument @var{class} may be used to cause the input
133 array to be interpreted as a different value type. Possible values are
134 
135 @multitable {Option} {Characters}
136 @headitem Option @tab Characters
137 @item @qcode{"int8"} @tab 2
138 @item @qcode{"uint8"} @tab 2
139 @item @qcode{"int16"} @tab 4
140 @item @qcode{"uint16"} @tab 4
141 @item @qcode{"int32"} @tab 8
142 @item @qcode{"uint32"} @tab 8
143 @item @qcode{"int64"} @tab 16
144 @item @qcode{"uint64"} @tab 16
145 @item @qcode{"char"} @tab 2
146 @item @qcode{"single"} @tab 8
147 @item @qcode{"double"} @tab 16
148 @end multitable
149 
150 For example:
151 
152 @example
153 @group
154 hex2num (["402df854"; "41200000"], "single")
155  @result{} [2.7183; 10.000]
156 @end group
157 @end example
158 @seealso{num2hex, hex2dec, dec2hex}
159 @end deftypefn */)
160 {
161  octave_value retval;
162 
163  int nargin = args.length ();
164 
165  if (nargin < 1 || nargin > 2)
166  print_usage ();
167 
168  std::string type = "double";
169  if (nargin == 2)
170  type = args(1).xstring_value ("hex2num: CLASS must be a string");
171 
172  Array<std::string> val = args(0).cellstr_value ();
173 
174  // We always use big-endian order for hex digits.
175  bool is_float = type == "single" || type == "double";
176  bool swap_bytes = is_little_endian (is_float);
177 
178  if (type == "int8")
179  retval = octave_value (hex2num<octave_int8> (val, swap_bytes));
180  else if (type == "uint8")
181  retval = octave_value (hex2num<octave_uint8> (val, swap_bytes));
182  else if (type == "int16")
183  retval = octave_value (hex2num<octave_int16> (val, swap_bytes));
184  else if (type == "uint16")
185  retval = octave_value (hex2num<octave_uint16> (val, swap_bytes));
186  else if (type == "int32")
187  retval = octave_value (hex2num<octave_int32> (val, swap_bytes));
188  else if (type == "uint32")
189  retval = octave_value (hex2num<octave_uint32> (val, swap_bytes));
190  else if (type == "int64")
191  retval = octave_value (hex2num<octave_int64> (val, swap_bytes));
192  else if (type == "uint64")
193  retval = octave_value (hex2num<octave_uint64> (val, swap_bytes));
194  else if (type == "char")
195  retval = octave_value (hex2num<char> (val, swap_bytes));
196  else if (type == "single")
197  retval = octave_value (hex2num<float> (val, swap_bytes));
198  else if (type == "double")
199  retval = octave_value (hex2num<double> (val, swap_bytes));
200  else
201  error ("hex2num: unrecognized CLASS '%s'", type.c_str ());
202 
203  return retval;
204 }
205 
206 /*
207 %!assert (hex2num (["c00";"bff";"000";"3ff";"400"]), [-2:2]')
208 %!assert (hex2num (["c00";"bf8";"000";"3f8";"400"], "single"), single([-2:2])')
209 %!assert (hex2num ("ff", "uint8"), intmax ("uint8"))
210 %!assert (hex2num ("ffff", "uint16"), intmax ("uint16"))
211 %!assert (hex2num ("ffffffff", "uint32"), intmax ("uint32"))
212 %!assert (hex2num ("ffffffff", "uint32"), intmax ("uint32"))
213 %!assert (hex2num ("ffffffffffffffff", "uint64"), intmax ("uint64"))
214 */
215 
216 static inline unsigned char
217 nibble2hex (unsigned char ch)
218 {
219  if (ch >= 10)
220  ch += 'a' - 10;
221  else
222  ch += '0';
223 
224  return ch;
225 }
226 
227 static inline void
228 num2hex (const void *p, std::size_t n, char *hex, bool swap_bytes)
229 {
230  const unsigned char *cp = reinterpret_cast<const unsigned char *> (p);
231 
232  std::size_t k = 0;
233 
234  for (std::size_t i = 0; i < n; i++)
235  {
236  std::size_t j = (swap_bytes ? n - i - 1 : i);
237 
238  unsigned char ch = cp[j];
239 
240  hex[k++] = nibble2hex ((ch >> 4) & 0xF);
241  hex[k++] = nibble2hex (ch & 0xF);
242  }
243 }
244 
245 template <typename T>
246 Cell
247 num2hex (const Array<T>& v, bool swap_bytes)
248 {
249  const std::size_t nbytes = sizeof (T);
250  const std::size_t nchars = 2 * nbytes;
251 
252  octave_idx_type nel = v.numel ();
253 
254  string_vector sv (nel);
255 
256  const T *pv = v.data ();
257 
258  for (octave_idx_type i = 0; i < nel; i++)
259  {
260  char hex[nchars];
261 
262  num2hex (pv++, nbytes, hex, swap_bytes);
263 
264  sv[i] = std::string (hex, nchars);
265  }
266 
267  return Cell (v.dims (), sv);
268 }
269 
270 DEFUN (num2hex, args, ,
271  doc: /* -*- texinfo -*-
272 @deftypefn {} {@var{s} =} num2hex (@var{n})
273 @deftypefnx {} {@var{s} =} num2hex (@var{n}, "cell")
274 Convert a numeric array to an array of hexadecimal strings.
275 
276 For example:
277 
278 @example
279 @group
280 num2hex ([-1, 1, e, Inf])
281 @result{} "bff0000000000000
282  3ff0000000000000
283  4005bf0a8b145769
284  7ff0000000000000"
285 @end group
286 @end example
287 
288 If the argument @var{n} is a single precision number or vector, the returned
289 string has a length of 8. For example:
290 
291 @example
292 @group
293 num2hex (single ([-1, 1, e, Inf]))
294 @result{} "bf800000
295  3f800000
296  402df854
297  7f800000"
298 @end group
299 @end example
300 
301 With the optional second argument @qcode{"cell"}, return a cell array of
302 strings instead of a character array.
303 @seealso{hex2num, hex2dec, dec2hex}
304 @end deftypefn */)
305 {
306  int nargin = args.length ();
307 
308  if (nargin < 1 || nargin > 2)
309  print_usage ();
310 
311  bool as_cell = false;
312 
313  if (nargin == 2)
314  {
315  std::string opt = args(1).xstring_value ("num2hex: second argument must be a string");
316  if (opt == "cell")
317  as_cell = true;
318  else
319  error ("num2hex: unrecognized option '%s'", opt.c_str ());
320  }
321 
322  octave_value val = args(0);
323 
324  if (val.iscomplex ())
325  error ("num2hex: N must be real");
326 
327  Cell result;
328 
329  // We always use big-endian order for hex digits.
330  bool is_float = val.is_single_type () || val.is_double_type ();
331  bool swap_bytes = is_little_endian (is_float);
332 
333  if (val.is_int8_type ())
334  result = num2hex (val.int8_array_value (), swap_bytes);
335  else if (val.is_int16_type ())
336  result = num2hex<octave_int16> (val.int16_array_value (), swap_bytes);
337  else if (val.is_int32_type ())
338  result = num2hex<octave_int32> (val.int32_array_value (), swap_bytes);
339  else if (val.is_int64_type ())
340  result = num2hex<octave_int64> (val.int64_array_value (), swap_bytes);
341  else if (val.is_uint8_type ())
342  result = num2hex<octave_uint8> (val.uint8_array_value (), swap_bytes);
343  else if (val.is_uint16_type ())
344  result = num2hex<octave_uint16> (val.uint16_array_value (), swap_bytes);
345  else if (val.is_uint32_type ())
346  result = num2hex<octave_uint32> (val.uint32_array_value (), swap_bytes);
347  else if (val.is_uint64_type ())
348  result = num2hex<octave_uint64> (val.uint64_array_value (), swap_bytes);
349  else if (val.is_char_matrix ())
350  result = num2hex<char> (val.char_array_value (), swap_bytes);
351  else if (val.is_single_type ())
352  result = num2hex<float> (val.float_vector_value (), swap_bytes);
353  else if (val.is_double_type ())
354  result = num2hex<double> (val.vector_value (), swap_bytes);
355  else
356  err_wrong_type_arg ("num2hex", val);
357 
358  return (as_cell
359  ? octave_value (result)
360  : octave_value (result.string_vector_value ()));
361 }
362 
363 /*
364 %!assert (num2hex (-2:2),
365 %! ["c000000000000000";"bff0000000000000";"0000000000000000";"3ff0000000000000";"4000000000000000"])
366 %!assert (num2hex (single (-2:2)),
367 %! ["c0000000";"bf800000";"00000000";"3f800000";"40000000"])
368 %!assert (num2hex (intmax ("uint8")), "ff")
369 %!assert (num2hex (intmax ("uint16")), "ffff")
370 %!assert (num2hex (intmax ("uint32")), "ffffffff")
371 %!assert (num2hex (intmax ("uint32")), "ffffffff")
372 %!assert (num2hex (intmax ("uint64")), "ffffffffffffffff")
373 
374 %!assert (hex2num (num2hex (pi)), pi)
375 %!assert (hex2num (num2hex (single (pi)), "single"), single (pi))
376 
377 %!error num2hex ()
378 %!error num2hex (1,2)
379 %!error num2hex (1,"foo")
380 %!error num2hex (1,2,3)
381 %!error num2hex (1j)
382 */
383 
384 OCTAVE_END_NAMESPACE(octave)
void swap_bytes(void *ptr)
Definition: byte-swap.h:43
const T * data() const
Size of the specified dimension.
Definition: Array.h:663
const dim_vector & dims() const
Return a const-reference so that dims ()(i) works efficiently.
Definition: Array.h:503
T & xelem(octave_idx_type n)
Size of the specified dimension.
Definition: Array.h:524
octave_idx_type numel() const
Number of elements in the array.
Definition: Array.h:414
Definition: Cell.h:43
string_vector string_vector_value() const
Definition: Cell.cc:158
bool is_uint32_type() const
Definition: ov.h:724
int32NDArray int32_array_value() const
Definition: ov.h:956
uint16NDArray uint16_array_value() const
Definition: ov.h:965
int16NDArray int16_array_value() const
Definition: ov.h:953
int8NDArray int8_array_value() const
Definition: ov.h:950
bool is_int8_type() const
Definition: ov.h:706
bool is_single_type() const
Definition: ov.h:698
charNDArray char_array_value(bool frc_str_conv=false) const
Definition: ov.h:897
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:971
bool is_int16_type() const
Definition: ov.h:709
bool is_char_matrix() const
Definition: ov.h:628
bool is_int64_type() const
Definition: ov.h:715
bool iscomplex() const
Definition: ov.h:741
int64NDArray int64_array_value() const
Definition: ov.h:959
Array< double > vector_value(bool frc_str_conv=false, bool frc_vec_conv=false) const
octave_idx_type length() const
bool is_double_type() const
Definition: ov.h:695
bool is_int32_type() const
Definition: ov.h:712
uint8NDArray uint8_array_value() const
Definition: ov.h:962
Array< float > float_vector_value(bool frc_str_conv=false, bool frc_vec_conv=false) const
uint32NDArray uint32_array_value() const
Definition: ov.h:968
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
void print_usage(void)
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:988
void err_wrong_type_arg(const char *name, const char *s)
Definition: errwarn.cc:166
Array< T > hex2num(const Array< std::string > &val, bool swap_bytes)
Definition: hex2num.cc:92
Cell num2hex(const Array< T > &v, bool swap_bytes)
Definition: hex2num.cc:247
bool words_little_endian()
Definition: mach-info.cc:83
float_format native_float_format()
Definition: mach-info.cc:67
@ flt_fmt_ieee_little_endian
Definition: mach-info.h:43
T octave_idx_type m
Definition: mx-inlines.cc:781
octave_idx_type n
Definition: mx-inlines.cc:761
return octave_value(v1.char_array_value() . concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string()) ? '\'' :'"'))